The following is only a summary of the core of the problem.
I defined the class that has one dependency property is as below.
public class TestMap : DependencyObject
{
public bool TestProperty
{
get { return (bool)GetValue(TestPropertyProperty); }
set { SetValue(TestPropertyProperty, value); }
}
public static readonly DependencyProperty TestPropertyProperty =
DependencyProperty.Register("TestProperty", typeof(bool), typeof(TestMap), new PropertyMetadata(false));
}
Now I'm going to use the above class in the UserControl.
The following code is an example.
public class UserControl1 : Control
{
public TestMap TestMap { get; set; }
}
And I used the UserControl1 in the MainWindow like below.
<Window>
<Grid>
<local:UserControl1 />
</Grid>
</Window>
The above code works well but If I try to binding to the TestMap.TestProperty what I should do?
I tried the following code but it doesn't work. (Assume ViewModel is bound)
<Window>
<Grid>
<local:UserControl1 TestMap.TestProperty="{Binding ViewModelProperty}"/>
</Grid>
</Window>
The error message for the above code is as below image.
The error message is
"It can't found connectable 'TestProperty' property in the 'TestMap' format."
Thanks for reading.
You can't bind to a nested property, i.e. a property TestProperty of the object returned by the property TestMap, like this in XAML. It's not supported.
You should either move the TestProperty to the UserControl, set it programatically, or consider using an attached dependency property that is settable on any object of a specific type.
Related
I have a class "Application" which has a static member(MainWindow, inherits from DXWindow) like the following:
public static MainWindow MainWindowInstance
I instantiate and display it:
MainWindowInstance = new MainWindow();
MainWindowInstance.ShowDialog();
MainWindow has a property:
private Thickness _localAttachmentsButtonsMargin = new Thickness(0, 0, 5, 0);
public Thickness LocalAttachmentsButtonsMargin {
get {
return _localAttachmentsButtonsMargin;
}
set {
_localAttachmentsButtonsMargin = value;
NotifyPropertyChanged("LocalAttachmentsButtonsMargin");
}
}
I need to access the LocalAttachmentsButtonsMargin property from many other controls (like UserControls) in xaml.
What I tried so far is:
--> Access it with x:Static:
Margin="{Binding Path=MainWindowInstance.LocalAttachmentsButtonsMargin, Source={x:Static Application.Current}}"
--> Giving the UserControl a x:Name ("UcTmp"), provide a getter in the UserControl and access the property from code behind
Margin="{Binding ElementName=UcTmp, Path=LocalAttachmentsButtonsMargin}}"
public Thickness LocalAttachmentsButtonsMargin {
get {
Application.MainWindowInstance.LocalAttachmentsButtonsMargin;
}
}
But none of them both seems to work. I think I need the xaml equivalent to the one line of code in the getter given above.
An additional requirement is that the property in MainWindow needs to be updatable. Ideally the solution is able to update the view which is represented throught the UserControl if the property will be updated though some code.
Since you can only bind to properties, you should define MainWindowInstance as such:
public static MainWindow MainWindowInstance { get; set; }
It cannot be a field:
public static MainWindow MainWindowInstance;
I am using WPF and I have a custom datagrid. What I would like to do is add a property (CustomGridCommands) to that grid which I can set in xaml from any view.
What I have at the moment is as follows (I have changed the code a bit to simplify it):
Custom grid C# code:
public class CustomWPFDataGrid : DataGrid, INotifyPropertyChanged
{
public static readonly DependencyProperty CustomContextMenuCommandsProperty =
DependencyProperty.Register("CustomContextMenuCommands",
typeof (ObservableCollection<WPFBaseCommand>),
typeof (CustomWPFDataGrid));
[Bindable(true)]
public ObservableCollection<WPFBaseCommand> CustomContextMenuCommands
{
get { return (ObservableCollection<WPFBaseCommand>) GetValue(CustomContextMenuCommandsProperty); }
set { SetValue(CustomContextMenuCommandsProperty, value); }
}
...
...
}
XAML code:
<common:CustomWPFDataGrid
ItemsSource="{Binding Path=ItemList}"
CustomContextMenuCommands="{Binding Path=CustomGridCommands, Mode=TwoWay}">
....
</common:CustomWPFDataGrid >
The object I have bound to the view that contains the grid is as follows:
public class TestViewModel
{
public ObservableCollection<TestDisplayViewModel> ItemList { get; set; }
public ObservableCollection<WPFBaseCommand> CustomGridCommands;
public TestViewModel()
{
... population of objects here
}
When I run this, and check the value of the property (CustomContextMenuCommands) in the datagrid, it is always null.
Any ideas what I am doing wrong?
EDIT
The setter of the "CustomContextMenuCommands" is never hit.
CustomGridCommands in your ViewModel is a field, View cannot use it. If you make it a public property, then it will become accessible. More details on what can be used as binding source can be found on MSDN - Binding Sources.
If using WPF 4.5, static properties can also be used for binding, as described in release notes.
I tried to implement something earlier this afternoon and have not been able to get it working the way I want.
If I have some XAML like the following (I know it is not complete but it is for illustrative purposes only)...
<Window>
<StackPanel>
<Button />
<UserControl1>
</StackPanel>
</Window>
...and UserControl1 exposes a command property (currently, I am using a RelayCommand for this), how do I bind the Button in the Window to this command. I tried to expose a property in the Window that is bound to the command property in UserControl1 so that I could turn around and re-bind this to the Button but the property in the Window is always null. This pattern seems to work for another property (an integer value). Is there a better way to do this? Is there something with the command that prevents this from occuring?
(1) define your command as a DepenedecyProperty and implement it .
(2) bind the Command property in the button the the MyCommand Property in your UserControl.
public Class UserControl1 : UserControl
{
public UserControl1()
{
MyCommad = new RelayCommand
(
() => { // do some stuff in execute delegate},
() => { return true ;}
);
}
public bool MyCommand
{
get { return (ICommand)GetValue(MyCommandProperty); }
set { SetValue(MyCommandProperty, value); }
}
public static readonly DependencyProperty MyCommandProperty =
DependencyProperty.Register("MyCommand", typeof(ICommand),new FrameworkPropertyMetadata(null));
}
xaml :
<Window>
<StackPanel>
<Button Command={Binding ElementName=control1,Path=MyCommand,Mode=OneWay/>
<UserControl1 x:Name="control1" />
</StackPanel>
</Window>
I am doing a simple WPF application using MVVM and I am having trouble binding to the SelectedItem property of the combobox.
The setter of the bound property does not get called, and there is no output in the debug windows telling me it is not able to bind (I assume it is able to).
This is .NET 3.5, I made a small example that has the same problem.
In XAML:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox IsDropDownOpen="False" IsReadOnly="False" ItemsSource="{Binding Path=Printers}" SelectedIndex="0" SelectedItem="{Binding Path=Printer.SelectedPrinter, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Name="cmbPrinters" />
</StackPanel>
</Window>
View code behind:
using System.Windows;
public partial class Window1 : Window
{
ViewModel viewmodel;
public Window1()
{
viewmodel = new ViewModel();
this.DataContext = viewmodel;
InitializeComponent();
}
}
View model:
using System;
using System.Collections.ObjectModel;
public class ViewModel
{
public ViewModel()
{
Printers = new ObservableCollection<string>() { "test", "test2" };
Printer = new PrinterViewModel();
}
public PrinterViewModel Printer { get; set; }
public ObservableCollection<string> Printers { get; set; }
}
PrinterViewModel:
using System.Windows;
using System.Diagnostics;
public class PrinterViewModel : DependencyObject
{
public string SelectedPrinter
{
get { return (string)GetValue(SelectedPrinterProperty); }
set
{
SetValue(SelectedPrinterProperty, value);
Debug.WriteLine("!!!!!! SelectedPrinter setter called");
}
}
public readonly DependencyProperty SelectedPrinterProperty =
DependencyProperty.Register("SelectedPrinter", typeof(string), typeof(PrinterViewModel), new UIPropertyMetadata());
}
Can anyone see what I am doing wrong here?
The problem here is that you have a misunderstanding about how the Silverlight dependency property system works.
When the value of a dependency property changes, Silverlight doesn't go through the properties you've defined (such as SelectedPrinter) to set the value of the dependency property. The Silverlight dependency property mechanism keeps track of all of the values of dependency properties, and when the value of one of these properties changes, Silverlight changes its value directly without calling your code to do so. In particular, it will not call your property setter. This should explain why your debugging message wasn't appearing.
The getter in a property that uses a dependency property, such as your SelectedPrinter property, should contain only a call to GetValue, and the setter should contain only a call to SetValue. You shouldn't add any code to the getter or setter, as doing this will not achieve what you want.
Furthermore, you are using dependency properties in the view-model layer. This is not where they are intended to be used. Dependency properties are only intended to be used in the view-layer. Your view-model classes should instead be implementing INotifyPropertyChanged rather than extending DependencyObject.
It is possible to bind two dependency properties together. This is permitted, and occasionally it comes in useful to wire together two dependency properties in the view-layer. In fact, the bindings in your example were working, which explains why you weren't getting any messages about problems with bindings.
why inherit from DependencyObject when doing mvvm?`
public class PrinterViewModel : INotifyPropertyChanged
{
private string _selected;
public string SelectedPrinter
{
get { return this._selected; }
set
{
_selected= value;
OnPropertyChanged("SelectedPrinter");
}
}
}
now your code should work
I have two UserControls (uc1 and uc2) loading into a third UserControl (shell). Shell has two properties, uc1 and uc2, of type UserControl1 and UserControl2, and each have a DependencyProperty registered to their own classes called IsDirty:
public static readonly DependencyProperty IsDirtyProperty = DependencyProperty.Register("IsDirty", typeof (bool), typeof (UserControl1));
public bool IsDirty
{
get { return (bool) GetValue(IsDirtyProperty); }
set { SetValue(IsDirtyProperty, value); }
}
(same code for UserControl2)
Shell has TextBlocks bound to the IsDirty properties:
<TextBlock Text="{Binding ElementName=shell, Path=Uc1.IsDirty}"/>
<TextBlock Text="{Binding ElementName=shell, Path=Uc2.IsDirty}"/>
When I change the values of IsDirty in uc1 and uc2, Shell never gets notified. What am I missing? UserControl is descendant of DependencyObject...
The same behavior occurs if I have regular properties notifying changes via INotifyPropertyChanged.
If I raise a routed event from uc1 and uc2, bubbling up to Shell, then I can catch the Dirty value and everything works, but I shouldn't have to do that, should I?
Thanks
Edit: The answer is to raise property changed event on the Uc1 and Uc2 properties or make them DPs.
I tried reproducing your problem using a simple setup, and it works fine for me. I'm not sure though if this setup is correct enough to replicate your situation. Anyway, I'm posting it just in case. It might be helpful:
XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
x:Name="shell"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Click="Button_Click">Click</Button>
<TextBlock Text="{Binding ElementName=shell, Path=Uc1.IsDirty}"/>
</StackPanel>
</Window>
Code-Behind:
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MyUserControl uc1 = new MyUserControl();
public MyUserControl Uc1
{
get { return this.uc1; }
}
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.uc1.IsDirty = !this.uc1.IsDirty;
}
}
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
}
public bool IsDirty
{
get { return (bool)GetValue(IsDirtyProperty); }
set { SetValue(IsDirtyProperty, value); }
}
// Using a DependencyProperty as the backing store for IsDirty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsDirtyProperty =
DependencyProperty.Register("IsDirty", typeof(bool), typeof(UserControl), new UIPropertyMetadata(false));
}
}
Karmicpuppet's answer works well. However it didn't solve my problem because Shell is also of type UserControl. For it to work I needed to raise the property changed on Uc1 and Uc2. When I declared them as DependencyProperties all worked as expected. Duh!