我在WPF演示项目中很挣扎。我必须认识到我有很多约束使代码变得复杂。
核心元素是一个从UserControl继承的控件。我想让它的代码隐藏尽可能的轻。另外,我想在ControlTemplate中使用其XAML。它的C#代码应位于专用的ViewModel中(此示例适用于大型项目,具有专用的viewModel可以通过将所有viewmodel分组来提供帮助。但是无论如何,这是强制性的)。最后但并非最不重要的一点,我想将此控件的2个属性绑定到外部属性。
这是我的MainWindow.xaml文件:
<Window x:Class="ViewModel_defined_in_ControlTemplate.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:ViewModel_defined_in_ControlTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<StackPanel>
<local:MyUserControl Template="{StaticResource TextBoxTemplate}"
NomPersonne="sg"/>
<Button Content="Click me!" Command="{Binding ElementName=MyViewModel,Path=ChangeTextBoxContent}" Width="100" HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
</Window>
该按钮仅更改NomPersonne依赖项属性的值(请参见下文)。MyDictionary.xaml包含ControlTemplate:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ViewModel_defined_in_ControlTemplate">
<ControlTemplate x:Key="TextBoxTemplate" TargetType="{x:Type local:MyUserControl}">
<Grid>
<Grid.DataContext>
<local:MyViewModel/>
</Grid.DataContext>
<TextBox Width="50" HorizontalAlignment="Left" Text="{TemplateBinding NomPersonne}"/>
</Grid>
</ControlTemplate>
</ResourceDictionary>
我不知道将依赖项属性放在何处,以及如何访问它。我试图将其放在MyUserControl中:
namespace ViewModel_defined_in_ControlTemplate
{
public partial class MyUserControl : UserControl
{
public string NomPersonne
{
get { return (string)GetValue(NomPersonneProperty); }
set { SetValue(NomPersonneProperty, value); }
}
public static readonly DependencyProperty NomPersonneProperty =
DependencyProperty.Register("NomPersonne", typeof(string), typeof(MyUserControl), new PropertyMetadata(""));
}
}
现在可以从MyUserCOntrol的XAML访问它,但是后来我不知道如何访问它才能使按钮的命令更改属性:
namespace ViewModel_defined_in_ControlTemplate
{
public class MyViewModel : ViewModelBase
{
public RelayCommand ChangeTextBoxContent = new RelayCommand(() =>
{
//...
}, () => true);
}
}
我希望在视图模型中具有依赖项属性,但是在这种情况下,如何在MyUserControl的XAML中访问MainWindow中的依赖项?
谢谢。
您应在视图模型中添加source属性,将UserControl
的target属性绑定到此模型,并在视图模型中更新source属性:
<local:MyUserControl Template="{StaticResource TextBoxTemplate}"
NomPersonne="{Binding Name}"/>
查看模型:
public class MyViewModel : ViewModelBase
{
public RelayCommand ChangeTextBoxContent = new RelayCommand(() =>
{
Name = "...":
}, () => true);
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
...
}
您还应该在窗口中而不是在DataContext
中设置ResourceDictionary
:
<Window x:Class="ViewModel_defined_in_ControlTemplate.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:ViewModel_defined_in_ControlTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<local:MyUserControl Template="{StaticResource TextBoxTemplate}"
NomPersonne="{Binding Name}"/>
<Button Content="Click me!" Command="{Binding ChangeTextBoxContent}" Width="100" HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
</Window>
ResourceDictionary
应该只定义模板:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ViewModel_defined_in_ControlTemplate">
<ControlTemplate x:Key="TextBoxTemplate" TargetType="{x:Type local:MyUserControl}">
<Grid>
<TextBox Width="50" HorizontalAlignment="Left"
Text="{TemplateBinding NomPersonne}"/>
</Grid>
</ControlTemplate>
</ResourceDictionary>
给UserControl一个ViewModel是一个非常糟糕的主意。您刚刚偶然发现了这种方法的许多问题之一。无法在ViewModel中定义DependecyProperty,只有代码隐藏。您将必须在代码隐藏中订阅ViewModel的PropertyChanged,并且每次ViewModel中的值更改时,都要在代码隐藏中更新各自的DependencyProperties。这也必须以其他方式解决。当DependencyProperty更改时,您必须在ViewModel中对其进行更新。做到这一点并非不可能,但这确实很丑陋(相信我,我已经做到了;以后再也不会)。
[另一个问题是将UserControl的DataContext(在代码隐藏或XAML中)设置为ViewModel。如果直接在UserControl上设置它,绑定将不起作用。解决方法是设置UserControl的第一个子级的DataContext(同样,不要这样做)。
带有ViewModel的UserControl是一个非常糟糕的主意。但这并不是说您的后台代码应包含所有代码。您总是可以将执行一些高级逻辑的方法提取到各自的类中。静态方法可以在任何地方调用,甚至可以在代码后面调用。