I am having a UserControl called ChartControl which displays a chart. To determine that the ChartControl is initialized, it needs to run a command which is defined inside a ViewModel called DiagnosisViewModel. The Command gets instantiated inside this ViewModel. After the ChartControl is loaded, it should execute the Command, but at this point, it is still null. It seems like that the Binding of the Command does not work as expected. Let the explain it better with code:
ChartControl.xaml
<UserControl x:Class="GoBeyond.Ui.Controls.Charts.ChartControl"
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"
mc:Ignorable="d"
x:Name="ThisChartControl"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
</UserControl>
ChartControl.xaml.cs
public partial class ChartControl : INotifyPropertyChanged
{
public ChartControl()
{
Loaded += OnLoaded;
}
public static readonly DependencyProperty ReadyCommandProperty = DependencyProperty.Register(
nameof(ReadyCommand), typeof(ICommand), typeof(ChartControl), new PropertyMetadata((o, args) =>
{
Debug.WriteLine("Ready Command updated");
}));
public ICommand ReadyCommand
{
get { return (ICommand)GetValue(ReadyCommandProperty); }
set { SetValue(ReadyCommandProperty, value); }
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Initialize the ChartControl with some values
...
// At this point the Command is ALWAYS null
ReadyCommand?.Execute(this);
}
}
ChartControl defined in DiagnosisView.xaml
<charts:ChartControl ReadyCommand="{Binding FrequencyReadyCommand}" />
DiagnosisViewModel
public class DiagnosisViewModel : ViewModelBase // ViewModelBase derives from Prisms BindableBase
{
public DiagnosisViewModel(...)
{
FrequencyReadyCommand = new DelegateCommand<ChartControl>(OnFrequencyChartReady);
}
public ICommand FrequencyReadyCommand
{
get => _frequencyReadyCommand;
set => SetProperty(ref _frequencyReadyCommand, value);
}
}
The getter of FrequencyReadyCommand seems never gets called, so I think there is any issue with my binding here. I tried with multiple Modes and UpdateSourceTriggers, but I cannot find out, what I am doing wrong here
You should invoke the command in the dependency property callback instead of handling the Loaded event:
public static readonly DependencyProperty ReadyCommandProperty = DependencyProperty.Register(
nameof(ReadyCommand), typeof(ICommand), typeof(ChartControl), new PropertyMetadata((o, args) =>
{
ChartControl chartControl = (ChartControl)o;
chartControl.ReadyCommand?.Execute(null);
}));
Unlike the Loaded event, the callback is always invoked after the property has been set.
Related
I've made a user control which contains a command, to be called in response to a certain event. This command is a dependency property. I want to use it in the main window like this:
<local:myUserControl Command="{Binding someCommand}"/>
The "myCommand" is the dependency property I created for this user control. And I bind it to a command of the view model of the main window ("someCommand").
The problem is that I am setting the datacontext of my usercontrol (I have a view model for it), and it seems to reset the "Command" to null… Here is the code-behind of my view model:
public partial class myUserControl : UserControl, ICommandSource
{
public myUserControl()
{
this.DataContext = new myViewModel();
InitializeComponent();
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(myUserControl), new PropertyMetadata(null));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(myUserControl), new PropertyMetadata(0));
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(myUserControl), new PropertyMetadata(null));
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Command.Execute(this.CommandParameter);
}
}
The code of my user control could be the Following:
<UserControl x:Class="myApp.myUserControl"
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:myApp"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock MouseUp="TextBlock_MouseUp">
</TextBlock>
</Grid>
</UserControl>
(I know that this element seems a bit silly (or useless), but I have simplified it to test what didn't worked and also in order to ask a rather simple question).
I have discovered that, if I comment the "this.DataContext = new myViewModel();" line, the binding to the command works perfectly. And when I uncomment this line and put a breakpoint in the "TextBlock_MouseUp", the "Command" property is equal to null...
Would there be a way to resolve this problem? I have some complicated code in my view model (so I'm quite forced to keep this line "this.DataContext = new myViewModel();"), and I am not sure I could find another solution than having a "Command" dependency property in my user control…
To be sure I give a maximum of informations, I have the following code in the view model of my main window:
public ICommand someCommand { get; set; }
//Constructor
public MainWindowViewModel()
{
this.someCommand = new RelayCommand((obj) => { return true; },
(obj) =>
{
//I put a breakpoint here
int dummy = 0;
});
}
(The RelayCommand class is a standard RelayCommand class, with a "Predicate" CanExecute and an "Action Execute).
I hope this question is not a duplicate… I have found several similar question, but they did not seem to answer mine...
I'm really sorry for this question which was in fact a bit silly. I hadn't understand very well what happens during a binding. I thought that this code line in the MainWindow…
<local:myUserControl Command="{Binding someCommand}"/>
…would have made an attempt to bind the UserControl's "Command" property to the "someCommand" of the datacontext of the MainWindow. In fact, as #elgonzo pointed out, the binding looks up in the UserControl's datacontext for the "someCommand" property (and not in the MainWindow's datacontext!!). Therefore, setting the UserControl's datacontext with this line…
this.DataContext = new myViewModel();
...was preventing the binding to be correctly done (since it looks for the "someCommand" property of the UserControl's datacontext, which is now "myViewModel", which does not contain "someCommand"...).
To fix this, I had to change the binding like this:
<local:myUserControl Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.someCommand}"/>
I've found this solution here: https://stackoverflow.com/a/1127964/11609068.
Maybe it is not the best way to do it (the "Path= DataContext. someCommand" make me think this, it doesn't seem very elegant), but it works. Another way to do it is to name the MainWindow (x:Name="someName"), so that the binding is a bit simpler:
<local:myUserControl Command="{Binding ElementName=someName, Path=DataContext.someCommand}"/>
Again, sorry and many thanks to #elgonzo.
I have a UserControl called LoginControl where I have defined a Command:
//This is LoginControl
public ICommand LoginCommand
{
get { return (ICommand)GetValue(LoginCommandProperty); }
set { SetValue(LoginCommandProperty, value); }
}
public static readonly DependencyProperty LoginCommandProperty =
DependencyProperty.Register("LoginCommand",
typeof(ICommand),
typeof(LoginControl));
I have a button in the LoginControl where the Click event calls this event handler:
private void Login_Click(object sender, RoutedEventArgs e)
{
LoginCommand.Execute(passwordBox.Password);
}
Now I have another UserControl called SettingsControl where I have included the LoginControl:
<local:LoginControl Grid.Row="1" Margin="5" LoginCommand="{Binding MyCommand}"/>
The DataContext of the SettingsControl is set to itself : DataContext="{Binding RelativeSource={RelativeSource Self}}
SettingsControl is defined like this:
public partial class SettingsControl : UserControl
{
public SettingsControl()
{
InitializeComponent();
MyCommand = new RelayCommand(o => MessageBox.Show("YESSS!"));
}
public ICommand MyCommand { get; set; }
}
When the Login_Click event handler is called, the code throws a NullReferenceException (MyCommand is null). I don't understand why. The MyCommand is initialised as you can see here. When I am initialising the LoginControl, I pass the MyCommand to it. So I don't understand why it should be null.
After like 2 hours thinking, I have found the solution.
Move MyCommand initialisation above InitializeComponent (Thanks to #Spawn for the suggestion)
Give a name to SettingsControl in xaml. Then change the LoginCommand binding as follows:
{Binding MyCommand, ElementName=settingControl}
And this worked. I am guessing the previous binding was looking for the DataContext of the LoginControl itself. By specifying the ElementName, wpf knows what DataContext I wanted (i.e. the DataContext of the SettingsControl).
I have extended the functionality of a textbox, below is part of the class with an attached property RegularExpressionEnabled:
public class AlphaNumericTextBox : TextBox
{
#region DependencyProperties
public static readonly DependencyProperty RegularExpressionEnabledProperty =
DependencyProperty.Register("RegularExpressionEnabled", typeof(bool), typeof(AlphaNumericTextBox),
new UIPropertyMetadata(false));
public bool RegularExpressionEnabled
{
get { return (bool)GetValue(RegularExpressionEnabledProperty); }
set { SetValue(RegularExpressionEnabledProperty, value); Console.WriteLine("RegularExpressionEnabled:" + (bool)value); }
}
}
This class is then incorporated into a UserControl.
XAML:
<UserControl x:Class="Libs_ViewLevel.Controls.FilterItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Libs_ViewLevel"
xmlns:ctrls="clr-namespace:Libs_ViewLevel.Controls"
x:Name="UserControl">
<Grid x:Name="LayoutRoot" Height="Auto" >
<ctrls:AlphaNumericTextBox x:Name="AlphaNumericTbx"
Grid.ColumnSpan="2"
Text="{Binding AlphaNumericValue}"
RegularExpressionEnabled="{Binding RegExpressionEnabled}" />
</Grid>
</UserControl>
Code-Behind:
public partial class FilterItemControl : UserControl
{
public FilterItemControl()
{
InitializeComponent();
this.DataContext = this;
}
public bool RegExpressionEnabled
{
get { return (bool)GetValue(RegExpressionEnabledProperty); }
set { SetValue(RegExpressionEnabledProperty, value); Console.WriteLine("RegExpressionEnabled:" + (bool)value); }
}
public static readonly DependencyProperty RegExpressionEnabledProperty =
DependencyProperty.Register("RegExpressionEnabled", typeof(bool), typeof(FilterItemControl),
new UIPropertyMetadata(false));
I have placed the UserControl in a Window and in the code-behind of this window, placed this statement: txtbox.RegExpressionEnabled = true;
In both RegularExpressionEnabled and RegExpressionEnabled you can see that I have placed a Console.Write().
The RegExpressionEnabled prints to the Output screen, however it does not get to the second, RegularExpressionEnabled.
Something is wrong in my binding on AlphaNumericTextBox RegularExpressionEnabled="{Binding RegExpressionEnabled}", can someone point me in the right direction?
In the code behind of FilterItemControl, RegExpressionEnabled property should be a standard property as this is what your new textbox is binding to. You don't need the dependency property in the FilterItemControl code behind either as you already have it in the new textbox.
Now instead of setting txtbox.RegExpressionEnabled = true (which actually should be txtbox.RegularExpressionEnabled = true), you set the RegExpressionEnabled property to true and the textbox will bind to this.
You will also need to raise the PropertyChanged event in the setter of the RegExpressionEnabled property to trigger the databinding.
I have a very simple example of a DependencyProperty I've registered on a UserControl which is not quite working as expected.
I am binding this property to a DP (called Test) in MainWindow which seems to be firing the OnPropertyChanged events every time it is changed, but the DependencyProperty in my UserControl that is the target of this binding only seems to get notified the first time this property is changed.
Here is what I'm trying to do in code:
My UserControl:
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty ShowCategoriesProperty =
DependencyProperty.Register("ShowCategories", typeof(bool), typeof(UserControl1), new FrameworkPropertyMetadata(false, OnShowsCategoriesChanged));
public UserControl1()
{
InitializeComponent();
}
public bool ShowCategories
{
get
{
return (bool)GetValue(ShowCategoriesProperty);
}
set
{
SetValue(ShowCategoriesProperty, value);
}
}
private static void OnShowsCategoriesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//this callback is only ever hit once when I expect 3 hits...
((UserControl1)d).ShowCategories = (bool)e.NewValue;
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
Test = true; //this call changes default value of our DP and OnShowsCategoriesChanged is called correctly in my UserControl
Test = false; //these following calls do nothing!! (here is where my issue is)
Test = true;
}
private bool test;
public bool Test
{
get { return test; }
set
{
test = value;
OnPropertyChanged("Test");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string property)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
}
MainWindow.xaml
<Window x:Class="Binding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:Binding"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<uc:UserControl1 ShowCategories="{Binding Test}" />
</Grid>
</Window>
The problem is that you're setting the ShowCategories value to itself:
((UserControl1)d).ShowCategories = (bool)e.NewValue;
This line is useless, since the value of ShowCategories has already been modified. That's why you're in the property changed callback in the first place. At first glance, this could be seen as a no-op: after all, you're simply setting a property value to its current value, which in WPF doesn't raise any change.
However, since the binding isn't two-way, changing the property value overwrites the binding. That's why there is no more callback raised. Simply remove the assignment in the callback and you're done.
Make the binding mode to be TwoWay.
My collection seems fine as I am able to use a ListView and it responds correctly to adds, however when I nest a listview into a UserControl it doesn't. I've provided the pertinent code.
I've created a UserControl derived class in this fashion:
public partial class MyCtrl: UserControl
{
#region Static Properties
public static readonly DependencyProperty ItemsSourceProperty =
ItemsControl.ItemsSourceProperty.AddOwner(
typeof(MyCtrl),
new PropertyMetadata(MyCtrl.ItemsSourcePropertyChangedCallback));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static void ItemsSourcePropertyChangedCallback(
DependencyObject controlInstance,
DependencyPropertyChangedEventArgs e)
{
MyCtrl myInstance=(MyCtrl)controlInstance;
myInstance.nestedList.ItemsSource=e.NewValue as IEnumerable;
}
}
With the XAML like this:
<UserControl x:Class="MyCtrl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ListView Name="nestedList" />
</Grid>
</UserControl>
My consuming XAML looks like this:
<MyCtrl x:Name="myInstance" ItemsSource="{Binding Path=MyCollection}" />
Where the collection is defined like this:
public static readonly DependencyProperty MyCollectionProperty =
DependencyProperty.Register("MyCollection",
typeof(ObservableCollection<MyObject>),
typeof(ConsumingObject),
new PropertyMetadata(new ObservableCollection<MyObject>());
public ObservableCollection<MyObject> MyCollection
{
get { return (ObservableCollection<MyObject>)this.GetValue(MyCollectionProperty); }
set { this.SetValue(MyCollectionProperty, value); }
}
maybe you want to register an event handler on CollectionChanged if your ItemSource is an ObservableCollection< T >
This questions maybe can help.
What is the DataContext of myInstance?
Was the code able to go to this line?
myInstance.nestedList.ItemsSource=e.NewValue as IEnumerable;
If yes then is nestedList null or not?