我有一个包含按钮、标签和文本框的自定义控件。此用户控件正在另一个具有 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}" />
您需要一个接受 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 绑定到该属性即可。
从您的解释和代码来看,我认为问题很可能是由于不同布局级别的数据上下文混乱造成的。
在
<views:ConveyorControl
声明级别,您期望数据上下文中存在 DesignMCP06PageViewModel
的实例。但在运行时,事实上,数据上下文中有一个 MainWindowViewModel
的实例,它不具有 RestartCommand
属性。
在不完全了解您的任务及其执行条件的情况下,我无法给出适合您的解决方案的准确答案。因此,我将建议 WPF 中使用的常用方法之一。如果它不适合您,请更详细地描述您的任务,如果您将解决方案上传到 GitHub 存储库并提供指向它的链接,那就更好了。
创建定位器。
这只是一个容器,其中包含需要在不同布局级别使用的各种数据的属性。大多数情况下,定位器属性是 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。