I have created a UserControl named AnswerUserControl
. XAML file looks like this (it's only button):
<UserControl
...>
<Button
DataContext="{Binding Path=ViewModel,
RelativeSource={
RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}
}}"
Command="{Binding Path=ClickCommand}"
Content="{Binding Path=Reply}" />
</UserControl>
And cs
file with dependency property to 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);
}
}
In Window I am adding this UserContol like this:
<StackPanel>
<local:AnswerUserControl
ViewModel="{Binding Path=VMA}" />
</StackPanel>
VMA
is an AnswerViewModel
in Window's ViewModel:
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);
}
The problem I am facing is that ClickCommand is executed only a few times. When I click slowly on the button it is executed from 4 to 6 times and when I click fast it is executed over 20 times and then it stops working. Where I am making a mistake?
I wrote a crude version of how it can be implemented. For the mainwindow we can use the following code.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowDataContext();
}
}
As seen above we assign a viewmodel to the mainwindow, in this viewmodel we can define a datacontext for the usercontrol. Giving us the following code;
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));
}
}
Do not mind the interface or the getter/setter, this is purely for demonstrating how binding can be notified that a property has changed.
Within the MainWindowDataContext we create a DataContextTest class. This viewmodel/datacontext class holds our command and can hold other bindings for the usercontrol.
public class DataContextTest
{
public ICommand ButtonCommand => new RelayCommand(executeThis);
private void executeThis()
{
Console.WriteLine($"VM clicked ");
}
}
For the front side where the xaml code and bindings reside I wrote the following things; A usercontrol that has a button, and in the mainwindow I inserted the usercontrol.
<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>
The mainwindow uses the MainWindowDataContext, this is being assigned in the constructor (see code block 1). The MainWindowDataContext has a getter setter for the usercontrol that needs a datacontext. The userControl gets notified when the datacontext changes.
When looking at the usercontrol xaml we see the following
<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>
With all this code it resolves to the following hierarchy for the viewmodels.
MainWindow -> MainWindowDataContext -> DataContextTest.
Because the MainWindow uses the UserControl we need to define a viewmodel for the MainWindow. Once that is set you can assign a datacontext/viewmodel to the usercontrol through binding on the front end.
No need for Dependecy Properties, a datacontext would suffice in most of the cases (: