我创建了一个UserControl
作为文件浏览工具,我希望将Commands
实现为DependencyProperties
以便加载和保存,以便我可以绑定Commands
来自ViewModel
1}}以便处理它们。
现在的问题是,如果我使用预定义的Commands
Open
或Save
并在我的Window
处理它,那么它可以使用{{1}来自我的Bindings
这些ViewModel
是Commands
...
以下代码是一个示例程序,我删除了对我的问题不重要的所有内容,因为代码太多了。
null
<UserControl x:Class="WpfApplication1.TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:models="clr-namespace:WpfApplication1.Models"
Height="21" Width="80" Margin="2">
<UserControl.Resources>
<models:UserControlViewModel x:Key="ViewModel"/>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource ViewModel}"/>
</UserControl.DataContext>
<Grid>
<Button Content="_Load" IsDefault="True"
Command="{Binding Path=ExecuteCommand, Source={StaticResource ViewModel}}"
CommandParameter="{Binding Path=LoadCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType ={x:Type local:TestControl}}}"/>
</Grid>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApplication1
{
public partial class TestControl : UserControl
{
public static readonly DependencyProperty LoadCommandProperty = DependencyProperty.Register(nameof(LoadCommand), typeof(ICommand), typeof(TestControl), new PropertyMetadata(null));
public ICommand LoadCommand
{
get { return (ICommand)GetValue(LoadCommandProperty); }
set { SetValue(LoadCommandProperty, value); }
}
public TestControl()
{
InitializeComponent();
}
}
}
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Prism.Commands;
namespace WpfApplication1.Models
{
public class UserControlViewModel
: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public DelegateCommand<ICommand> ExecuteCommand { get; }
private void ExecuteCommand_Executed(ICommand cmd) => cmd?.Execute("C:\\Test.txt");
private void Notify([CallerMemberName] string name = null)
{
if (name != null)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public UserControlViewModel()
{
ExecuteCommand = new DelegateCommand<ICommand>(ExecuteCommand_Executed);
}
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:models="clr-namespace:WpfApplication1.Models"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200">
<Window.Resources>
<models:MainWindowViewModel x:Key="ViewModel"/>
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource ViewModel}"/>
</Window.DataContext>
<Window.CommandBindings>
<CommandBinding Command="Open" Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<StackPanel VerticalAlignment="Center">
<local:TestControl LoadCommand="{Binding Path=OpenCommand, Source={StaticResource ViewModel}}"/>
<local:TestControl LoadCommand="Open"/>
</StackPanel>
</Window>
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CommandBinding_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
MessageBox.Show($"Window: {e.Parameter.ToString()}");
}
}
}
该窗口包含一个预定义的using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using Prism.Commands;
namespace WpfApplication1.Models
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public DelegateCommand<string> OpenCommand { get; }
private void OpenCommand_Executed(string file)
{
MessageBox.Show($"Model: {file}");
}
private void Notify([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public MainWindowViewModel()
{
OpenCommand = new DelegateCommand<string>(OpenCommand_Executed);
}
}
}
,以这种方式有效但Open Command
没有。
要运行此应用程序,您需要Prism.Wpf NuGet包。
答案 0 :(得分:0)
看起来你在这里挖出了奇怪的东西。但是要解释我的一个朋友,&#34;好消息是,这次癌症更容易治疗&#34;。
首先,我使用PresentationTraceSources.TraceLevel
对您的绑定进行了活检:
<local:TestControl
LoadCommand="{Binding
Source={StaticResource ViewModel},
Path=OpenCommand,
PresentationTraceSources.TraceLevel=High}"
/>
这就是我得到的:
System.Windows.Data Warning: 56 : Created BindingExpression (hash=34810426) for Binding (hash=11882558)
System.Windows.Data Warning: 58 : Path: 'OpenCommand'
System.Windows.Data Warning: 60 : BindingExpression (hash=34810426): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=34810426): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=34810426): Attach to WpfApplication1.TestControl.LoadCommand (hash=5114324)
System.Windows.Data Warning: 67 : BindingExpression (hash=34810426): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=34810426): Found data context element: <null> (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=34810426): Activate with root item UserControlViewModel (hash=33108977)
System.Windows.Data Warning: 108 : BindingExpression (hash=34810426): At level 0 - for UserControlViewModel.OpenCommand found accessor <null>
System.Windows.Data Error: 40 : BindingExpression path error: 'OpenCommand' property not found on 'object' ''UserControlViewModel' (HashCode=33108977)'. BindingExpression:Path=OpenCommand; DataItem='UserControlViewModel' (HashCode=33108977); target element is 'TestControl' (Name=''); target property is 'LoadCommand' (type 'ICommand')
System.Windows.Data Warning: 80 : BindingExpression (hash=34810426): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=34810426): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=34810426): TransferValue - using final value <null>
以下是发生的事情:在MainWindow.xaml
中,{StaticResource ViewModel}
正在TestControl
的上下文中查找FindResource
。效果与在TestControl
中调用public TestControl()
{
InitializeComponent();
var x = FindResource("ViewModel");
// Set breakpoint here and inspect x
;
}
:
TestControl
ViewModel
拥有自己的资源UserControlViewModel
,以便查找找到的内容。该资源是OpenCommand
,没有MainWindow
属性,在这种情况下,隐藏完全不同的资源MainWindow
中的同名资源。
我不知道你从哪里获得这个viewmodel资源方案,但你可以看到它是一个严重的错误。 TestControl
中的任何人都不必担心ExecuteCommand
内部使用的资源键。
幸运的是,没有必要创建该问题。你可以剔除一大堆代码,最终得到更简单,更健壮,更易于维护的东西。
所以,解决问题:
首先,不要将所有视图模型创建为资源,因为没有理由这样做会导致问题。将此DataContext
绑定与您拥有的绑定进行比较。你用这些东西获得了什么?没有。如果您不想设置<UserControl.DataContext>
<models:UserControlViewModel />
</UserControl.DataContext>
<Grid>
<Button Content="_Load" IsDefault="True"
Command="{Binding ExecuteCommand}"
CommandParameter="{Binding Path=LoadCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:TestControl}}}"/>
</Grid>
,将其用作DataContext 。
MainWindow
这里CommandBindings
应该是什么样的(忽略你不想要的<Window.DataContext>
<models:MainWindowViewModel />
</Window.DataContext>
<StackPanel VerticalAlignment="Center">
<!--
local:TestControl.DataContext is its own viewmodel, so we use RelativeSource
to get to the Window, and then we look at Window.DataContext for the main window
viewmodel.
-->
<local:TestControl
LoadCommand="{Binding DataContext.OpenCommand, RelativeSource={RelativeSource AncestorType=Window}}"
/>
</StackPanel>
事物):
DataContext
最后,由于一些现在必须显而易见的原因,UserControls创建自己的视图模型,这通常是不好的做法。我发现当他们继承父母的.toggle()
时,他们的困惑就更少了。但是我已经把你的设计抛到窗外一天了,所以我们将把它留下来。