问题描述
我已经创建了一个名为AnswerUserControl
的UserControl。 XAML文件如下所示(唯一的按钮):
<UserControl
...>
<Button
DataContext="{Binding Path=viewmodel,RelativeSource={
RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}
}}"
Command="{Binding Path=ClickCommand}"
Content="{Binding Path=Reply}" />
</UserControl>
public partial class AnswerUserControl : UserControl
{
public AnswerUserControl()
{
InitializeComponent();
}
public Answerviewmodel viewmodel
{
get
{
return (Answerviewmodel)GetValue(viewmodelProperty);
}
set
{
SetValue(viewmodelProperty,value);
}
}
public static readonly DependencyProperty viewmodelProperty =
DependencyProperty.Register("viewmodel",typeof(Answerviewmodel),typeof(AnswerUserControl));
}
Answerviewmodel类:
public class Answerviewmodel : viewmodelBase
{
private string reply;
public string Reply
{
get
{
return reply;
}
set
{
reply = value;
RaisePropertyChanged(nameof(Reply));
}
}
private ICommand clickCommand;
public ICommand ClickCommand
{
get
{
return clickCommand;
}
set
{
clickCommand = value;
RaisePropertyChanged(nameof(ClickCommand));
}
}
public Answerviewmodel(Action<int> click,string reply,int index)
{
void Click()
{
Console.WriteLine($"Answerviewmodel click at {index}");
click(index);
}
Reply = reply;
ClickCommand = new RelayCommand(Click);
}
}
在Window中,我像这样添加UserContol:
<StackPanel>
<local:AnswerUserControl
viewmodel="{Binding Path=VMA}" />
</StackPanel>
VMA
是Window的viewmodel中的Answerviewmodel
:
private Answerviewmodel vma;
public Answerviewmodel VMA
{
get
{
return vma;
}
set
{
vma = value;
RaisePropertyChanged(nameof(VMA));
}
}
static int number = 1;
public Homeviewmodel()
{
VMA = new Answerviewmodel(x => { Console.WriteLine($"Click Number: {number++}"); },"Reply",0);
}
我面临的问题是ClickCommand仅执行了几次。当我缓慢单击按钮时,它会执行4至6次,而当我快速单击按钮时,它将执行20次以上,然后它将停止工作。我在哪里弄错了?
解决方法
我写了一个如何实现的粗略版本。对于主窗口,我们可以使用以下代码。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowDataContext();
}
}
如上所示,我们向主窗口分配了一个视图模型,在这个视图模型中,我们可以为用户控件定义一个数据上下文。给我们下面的代码;
public class MainWindowDataContext : INotifyPropertyChanged
{
public MainWindowDataContext()
{
newDatacontext = new DataContextTest();
}
private DataContextTest _newDatacontext;
public DataContextTest newDatacontext
{
get => _newDatacontext;
set
{
if(_newDatacontext == value)
return;
_newDatacontext = value;
OnPropertyChanged(nameof(newDatacontext));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(name));
}
}
不要介意接口或getter / setter,这纯粹是为了演示如何通知绑定属性已更改。
在MainWindowDataContext内,我们创建一个DataContextTest类。这个viewmodel / datacontext类包含我们的命令,并且可以为usercontrol保留其他绑定。
public class DataContextTest
{
public ICommand ButtonCommand => new RelayCommand(executeThis);
private void executeThis()
{
Console.WriteLine($"VM clicked ");
}
}
对于xaml代码和绑定所在的前端,我编写了以下内容: 一个具有按钮的用户控件,在主窗口中插入了该用户控件。
<Window x:Class="Tryout.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:Tryout"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:UserControl1 DataContext="{Binding newDatacontext,UpdateSourceTrigger=PropertyChanged}"></local:UserControl1>
</Grid>
主窗口使用MainWindowDataContext,这是在构造函数中分配的(请参见代码块1)。 MainWindowDataContext为需要数据上下文的用户控件提供了一个getter设置方法。数据上下文更改时,userControl会收到通知。
在查看用户控件xaml时,我们看到以下内容
<UserControl x:Class="Tryout.UserControl1"
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:Tryout"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button Command="{Binding ButtonCommand}" Width="200" Height="100" Content="test"/>
</Grid>
使用所有这些代码,它可以解析为视图模型的以下层次结构。
MainWindow-> MainWindowDataContext-> DataContextTest。
因为MainWindow使用UserControl,所以我们需要为MainWindow定义一个视图模型。设置好之后,您可以通过前端绑定将datacontext / viewmodel分配给usercontrol。
不需要Dependecy属性,在大多数情况下,数据上下文就足够了(: