У меня есть пользовательский элемент управления, содержащий кнопки, метки и текстовые поля. Этот пользовательский элемент управления используется в другом представлении, имеющем ViewModel. Я хочу привязать обработчик событий щелчка из пользовательского элемента управления к команде в viewModel.
Код для представления:
public partial class ViewPage : UserControl
{
public ViewPage()
{
InitializeComponent();
}
private void CustomControl_RestartClick(object sender, RoutedEventArgs e)
{
Debug.WriteLine("clicked");
}
}
}
Код пользовательского элемента управления:
public partial class CustomControl : UserControl
{
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(CustomControl), new PropertyMetadata(String.Empty));
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly RoutedEvent RestartClickEvent = EventManager.RegisterRoutedEvent(nameof(RestartClick), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(CustomControl));
public event RoutedEventHandler RestartClick
{
add { AddHandler(RestartClickEvent, value); }
remove { RemoveHandler(RestartClickEvent, value); }
}
public CustomControl()
{
InitializeComponent();
}
public void OnRestartClick(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(RestartClickEvent));
}
}
У меня есть эта команда в ViewModel, к которой я хочу ее привязать:
public ICommand RestartCommand { get; private set; }
Я хочу иметь возможность делать что-то вроде
<views:CustomControl Title = "PS04-04" RestartClick = "{Binding RestartCommand}" />
ссылка на github: https://github.com/MaganaLu/MCP_WPF.git
К вашему сведению: просто предложение, но вы можете назвать свой элемент управления как-нибудь иначе, чем CustomControl
, хотя бы для того, чтобы избежать путаницы с четко определенной концепцией WPF «пользовательский элемент управления», означающей элемент управления, не производный от класса UserControl
, который не имеет связанного Страница XAML, для которой можно задать стиль и для которой необходимо определить шаблон элемента управления.
Как мне создать его для ICommand вместо RoutedEVent? @Клеменс
Как и любое другое свойство зависимостей. У вас уже есть один заголовок с именем типа string. Создайте еще один с именем Restart типа ICommand. В XAML пользовательского элемента управления привяжите его к кнопке «перезагрузка» с помощью <Button ... Command = {Binding Restart, RelativeSource = {RelativeSource AncestorType=UserControl}}"/>
.
Затем привяжите свойство RestartCommand, например <views:CustomControl Title = "PS04-04" Restart = "{Binding RestartCommand}"/>
Обратите внимание, что я откатил ваш вопрос к исходной проблеме. Пожалуйста, не меняйте содержание вопроса настолько существенно, что это сделает существующие ответы бессмысленными. Подумайте о том, чтобы принять ответ, а затем задайте новый вопрос, если у вас возникнет другая проблема.
Вам нужно свойство зависимостей, которое принимает ICommand.
public static readonly DependencyProperty FooCommandProperty =
DependencyProperty.Register(nameof(FooCommand), typeof(ICommand), typeof(CustomControl));
public ICommand FooCommand
{
get => (ICommand)GetValue(FooCommandProperty);
set => SetValue(FooCommandProperty, value);
}
Затем где-то в вашем пользовательском элементе управления (вероятно, в обработчике событий) вам нужно выполнить эту команду, когда что-то произойдет.
if (FooCommand != null && FooCommand.CanExecute(fooParameter))
{
FooCommand.Execute(fooParameter);
}
Когда все это будет готово, вы просто привяжете ICommand к свойству.
Или просто привяжите FooCommand к элементу управления в XAML UserControl.
@Clemens Я сделал это, но теперь он ищет команду в другой модели представления сети. Я получаю сообщение об ошибке «Ошибка System.Windows.Data: 40: ошибка пути BindingExpression: свойство RestartCommand не найдено в объекте» MainWindowViewModel (HashCode = 65300541) »», предполагается, что точка находится в другой модели представления.
@DuckQuackington Вы написали: «У меня есть эта команда в ViewModel, к которой я хочу ее привязать: public ICommand RestartCommand { get; private set; }
» - мы понятия не имеем, о какой модели представления вы говорите, и существует ли вообще более одной модели представления. Когда вы пишете {Binding RestartCommand}
, в текущем DataContext ожидается свойство RestartCommand. то есть текущая модель представления, какой бы она ни была. Вы нам не сказали.
@Клеменс Извините, что не предоставил достаточно информации. Я использую WPF впервые. Я действительно ценю вашу помощь. Я обновил вопрос. У меня есть «MainWindowViewModel», который содержит представление «MCP06Page».
Судя по вашим объяснениям и коду, я думаю, что проблема, скорее всего, вызвана путаницей контекстов данных на разных уровнях макета.
На уровне объявления <views:ConveyorControl
вы ожидаете экземпляр DesignMCP06PageViewModel
в контексте данных. Но на самом деле во время выполнения в контексте данных появляется экземпляр MainWindowViewModel
, который не имеет свойства RestartCommand
.
Не зная полностью Вашей задачи, условий ее выполнения, я не могу дать точный ответ, подходящий для Вашего решения. Поэтому я предложу один из распространенных подходов, используемых в WPF. Если вас это не устраивает, напишите подробнее о своей задаче и будет лучше, если вы загрузите решение в репозиторий GitHub и предоставите ссылку на него.
Создание локатора.
Это всего лишь контейнер со свойствами для различных данных, которые нужно использовать на разных уровнях вёрстки. Чаще всего свойства Locator являются отдельными экземплярами ViewModel. В простейшем случае (когда свойства инициализируются один раз при запуске приложения, обязательно ДО первого вызова локатора) нет необходимости реализовывать в локаторе интерфейс INotifyPropertyChanged.
public class Locator
{
public MainWindowViewModel MainVM { get; set; } = new();
public DesignMCP06PageViewModel MCP06_VM { get; set; } = new();
}
Вы объявляете экземпляр локатора в ресурсах приложения.
<Application.Resources>
<somens:Locator x:Key = "locator"/>
Доступ к свойствам локатора:
<Window x:Class = "-------------.MainWindow"
----------------------------------
DataContext = "{Binding MainVM, Source = {StaticResource locator}}">
<Grid>
<views:ConveyorControl x:Name = "binding"
Title = "PS04-04"
RestartCommand = "{Binding MCP06_VM.RestartCommand,
Source = {StaticResource locator}}"/>
</Grid>
</Window>
P.S. Ответ не актуален, так как после дополнительных уточнений и просмотра Решения на GitHub выяснилось, что переключение представлений происходит с помощью пакета Prism. По сути, вопрос автора сводится к тому, как правильно использовать Prism.
Привет @EldHasp Да, я не понимаю, почему контекст данных не взаимодействует с «MCP06ViewModel» и вместо этого переходит в MainWindowViewModel. Я создал пользовательский элемент управления UserControl в папке компонентов. Ссылка на github находится здесь: github.com/MaganaLu/MCP_WPF. Еще раз благодарю вас за помощь!
@DuckQuackington Я посмотрел твое решение. Оказывается, вы используете Prism и переключение страниц происходит через сервисы этого пакета. К сожалению, я не очень хорошо знаком с Призмой. Также советую сразу указывать информацию об использовании Prism в начале вашего вопроса. Было бы еще лучше, если бы вы указали это в названии темы.
Также имейте в виду, что «переключение взглядов» не имеет ничего общего с исходным вопросом, на который уже был дан ответ. @Duck, пожалуйста, задайте новый вопрос, если у вас есть другие проблемы. Избегайте вопросов-хамелеонов. Не забудьте принять ответ на исходный вопрос. См. stackoverflow.com/help/someone-answers
Целью привязки типа
{Binding RestartCommand}
будет свойство зависимости типаICommand
, например свойство Команда кнопки.