WPF 命令绑定只工作几次
WPF command binding works only a few times
我创建了一个名为 AnswerUserControl
的用户控件。 XAML 文件看起来像这样(只有按钮):
<UserControl
...>
<Button
DataContext="{Binding Path=ViewModel,
RelativeSource={
RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}
}}"
Command="{Binding Path=ClickCommand}"
Content="{Binding Path=Reply}" />
</UserControl>
和 cs
具有依赖性的文件 属性 到 AnswerViewModel
:
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 class:
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 class。这个 viewmodel/datacontext class 包含我们的命令并且可以包含用户控件的其他绑定。
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 setter 用于需要数据上下文的用户控件。当数据上下文更改时,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 分配给用户控件。
不需要依赖属性,在大多数情况下数据上下文就足够了 (:
我创建了一个名为 AnswerUserControl
的用户控件。 XAML 文件看起来像这样(只有按钮):
<UserControl
...>
<Button
DataContext="{Binding Path=ViewModel,
RelativeSource={
RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}
}}"
Command="{Binding Path=ClickCommand}"
Content="{Binding Path=Reply}" />
</UserControl>
和 cs
具有依赖性的文件 属性 到 AnswerViewModel
:
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 class:
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 class。这个 viewmodel/datacontext class 包含我们的命令并且可以包含用户控件的其他绑定。
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 setter 用于需要数据上下文的用户控件。当数据上下文更改时,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 分配给用户控件。
不需要依赖属性,在大多数情况下数据上下文就足够了 (: