I have trouble to understand how dependency properties can be used between C# and xaml code.
This is a smal code example of my question
XAML code:
<Window x:Class="WpfChangeTextApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Label Name="statusTextLabel" Content="{Binding StatusText}"></Label>
<Button Name="changeStatusTextButton" Click="changeStatusTextButton_Click">Change Text</Button>
</StackPanel>
C# code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string StatusText
{
get { return (string)GetValue(StatusTextProperty); }
set { SetValue(StatusTextProperty, value); }
}
// Using a DependencyProperty as the backing store for StatusText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StatusTextProperty =
DependencyProperty.Register("StatusText", typeof(string), typeof(MainWindow));
private void changeStatusTextButton_Click(object sender, RoutedEventArgs e)
{
StatusText = "Button clicked";
}
}
So, my trouble is that Label statusTextLabel dose not get updated when I click on the button. My trouble is that I don't know in what part of the code that I'm doing something wrong, is it in the xaml or in the C#? In the xaml I might doing something wrong in the Binding? Or have I missed doing something in the C# code?
By default, binding paths are relative to the DataContext property of the current element. You have not set it to anything, so it can't resolve the binding. If you want the StatusText property on your window class, then there are two approaches. One is to use a binding with a RelativeSource of FindAncestor to find the Window in the tree and bind to its properties directly:
<Label Name="statusTextLabel" Content="{Binding StatusText,
RelativeSource={RelativeSource AncestorType=Window}}"></Label>
The other is to set the DataContext of the Window to itself, so it will be inherited by the label. For example, in your constructor:
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
For most applications, you will actually want a separate class to represent the data, and you will set an instance of that class as the DataContext. You can also use ordinary CLR properties instead of dependency properties, although you will need to implement INotifyPropertyChanged if you want to UI to be informed when properties change. Dependency properties are more useful when you are writing a custom control and you want users to be able to set the properties using data binding.
Related
I have a WPF window which I have changed its borders, title bar and buttons for a custom one. Separately in a ResourceDictionary I have defined the Style that I want to apply to my WPF window.
Then I bind that style to my WPF Window using below line of code:
Style="{DynamicResource CustomWindowStyle}"
Please see all my code posted in another thread that I created to ask for other things. Refer to this thread to see the code (I want to avoid repeating here again).
Now what I am trying to do is to create some custom properties (MyCustomProperty01, MyCustomProperty02) to that WPF Window, some like below:
myDialogView.xaml:
<Window x:Class="myApp.Views.myDialogView"
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:v="clr-namespace:myApp.Views"
xmlns:vm="clr-namespace:myApp.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
Style="{StaticResource CustomWindowStyle}"
WindowStyle="None"
MyCustomProperty01 = "true"
MyCustomProperty02 = "true">
...and then in the code-behind of this WPF window (xaml.cs) I want to define those dependency properties MyCustomProperty01 and MyCustomProperty02, the ones you see above in the code snippet for the Window.
So I create those dependency properties in the code-behind as below.
myDialogView.xaml.cs (code-behind):
namespace myApp.Views
{
public partial class myDialogView: Window
{
public myDialogView()
{
InitializeComponent();
}
public static readonly DependencyProperty MyCustomProperty01Property =
DependencyProperty.Register("MyCustomProperty01",
typeof(bool),
typeof(myDialogView),
new FrameworkPropertyMetadata(false));
public bool MyCustomProperty01
{
get => (bool)GetValue(MyCustomProperty01Property );
set => SetValue(MyCustomProperty01Property , value);
}
public static readonly DependencyProperty MyCustomProperty02Property =
DependencyProperty.Register("MyCustomProperty02",
typeof(bool),
typeof(myDialogView),
new FrameworkPropertyMetadata(false));
public bool MyCustomProperty02
{
get => (bool)GetValue(MyCustomProperty02Property );
set => SetValue(MyCustomProperty02Property , value);
}
}
}
So when I start typing these dependency properties MyCustomProperty01 and MyCustomProperty01 for the window in the xaml view, intellisense looks like it is not recognizing those dependency properties. So what am I doing wrong? Could some tell me how I can achieve this with some code snippet?
You need to registerattached for this to work.
I'm not 100% sure why, I just know it won't work with the usual dependency property.
I think it's because it's in a window tag and it's "really" a myDialogView causes the issue.
Here's an example attached property I have:
public static readonly DependencyProperty TakeMapPictureProperty =
DependencyProperty.RegisterAttached(
"TakeMapPicture",
typeof(bool),
typeof(MainWindow),
new FrameworkPropertyMetadata(false
, new PropertyChangedCallback(TakeMapPictureChanged)
));
private static async void TakeMapPictureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public static void SetTakeMapPicture(UIElement element, bool value)
{
element.SetValue(TakeMapPictureProperty, value);
}
public static bool GetTakeMapPicture(UIElement element)
{
return (bool)element.GetValue(TakeMapPictureProperty);
}
And in the window tag
local:MainWindow.TakeMapPicture="{Binding CaptureMap}"
Where you might want to use the one dependency property in several user controls or windows, you can define the attached properties in a separate class. There are a couple of ways to do this but the one I prefer looks like:
public class MainWin : DependencyObject
{
public static readonly DependencyProperty MapFileProperty = DependencyProperty.RegisterAttached(
"MapFile",
typeof(string),
typeof(MainWin),
new FrameworkPropertyMetadata(string.Empty
, new PropertyChangedCallback(MapFileChanged)
));
And that is used inside a window tag:
local:MainWin.MapFile="{Binding Path=MapFile}"
You are trying to set the property on the wrong element (locally/attribute syntax):
<Window ... MyCustomProperty01="True" />
Of course, Window does not have a MyCustomProperty01 property.
But myDialogView has this property.
To set this property, either use a Style:
<Window>
<Window.Resources>
<Style TargetType="myDialogView">
<Setter Property="MyCustomProperty01" Value="True" />
</Style>
</Window.Resources>
</Window>
Or set it locally on the proper myDialogView element. But you would have to create a derived type (e.g., myExtendedDialogView), so that you can define myDialogView (the base type) as the root element.
Note how the root element is always the base type of the actual extending type: for example <Window /> of MainWindow or <UserControl /> of MyUserControl or <Page /> of MyPage or <Application /> of App.
Specify the partial class (the derived type that provides the code-behind file) using the x:Class directive on the root element:
<myDialogView x:Class="myExtendedDialogView"
MyCustomProperty01="True">
</myDialogView >
// Define the extended class as partial (with code-behind)
// and the base class that contains the custom properties as a simple class (no code-behind)
partial class myExtendedDialogView : myDialogView
{
public myExtendedDialogView() => InitializeComponent();
}
// Base class used as XAML root element.
// This type has no code-behind file (not partial)
class myDialogView : Window
{
// Dependency properties
}
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'm working on a "simple" case. I like to create a new custom control which implements a DependencyProperty. In the next step I like to create a binding for updating the properties in both directions. I've builded a simple sample for this case, but the binding doesn't seem to work. I've found a way for updating the DPControl's property by using the FrameworkPropertyMetadata, but I don't know whether it's also a good idea to use the OnPropertyChanged event.
HERE is my sample project:
My control contains simply a Label
<UserControl x:Class="WPF_MVVM_ListBoxMultiSelection.DPControl"
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:WPF_MVVM_ListBoxMultiSelection"
mc:Ignorable="d" Height="84.062" Width="159.641">
<Grid Margin="0,0,229,268">
<Label Content="TEST" x:Name="label" Margin="0,0,-221,-102"/>
</Grid>
</UserControl>
and implement a custom dependency property. Currently, I have also implemented the PropertyChanged method for the FramePropertyMetadata and set in this method the label's content, but I like to get it work in both directions.
public partial class DPControl : UserControl
{
public DPControl()
{
InitializeComponent();
}
public string MyCustomLabelContent
{
get { return (string)GetValue(MyCustomLabelContentProperty);}
set
{
SetValue(MyCustomLabelContentProperty, value);
}
}
private static void OnMyCustomLabelContentPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
DPControl control = (DPControl)source;
control.label.Content = e.NewValue;
}
public static readonly DependencyProperty MyCustomLabelContentProperty = DependencyProperty.Register(
"MyCustomLabelContent",
typeof(string),
typeof(DPControl),
new FrameworkPropertyMetadata(null,
OnMyCustomLabelContentPropertyChanged
)
);
I use this control simply in a Window by:
<local:DPControl MyCustomLabelContent="{Binding MyLabelContent, Mode=TwoWay}" Margin="72,201,286,34"/>
MyLabelContent is a property in the ViewModel, which has implemented also the INotifyPropertyChanged interface.
public class ViewModel_MainWindow:NotifyPropertyChanged
{
private string _myLabelContent;
public string MyLabelContent
{
get { return _myLabelContent; }
set { _myLabelContent = value;
RaisePropertyChanged();
}
}...
So how can I get it work: Using the binding feature with my new control on custom properties.
In your UserControl:
<Label
Content="{Binding MyCustomLabelContent, RelativeSource={RelativeSource AncestorType=UserControl}}"
x:Name="label" Margin="0,0,-221,-102"/>
And get rid of that property-changed callback. All you need is the Binding.
I like to get it work in both directions
To make the dependency property two-way by default:
public static readonly DependencyProperty MyCustomLabelContentProperty =
DependencyProperty.Register(
"MyCustomLabelContent",
typeof(string),
typeof(DPControl),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
I omitted the unnecessary property change handler.
It can't usefully be two-way now, because Label.Content can't generate its own value. If you want your UserControl to set the value in its codebehind, that's easy:
MyCustomLabelContent = "Some arbitrary value";
If you did the binding like I showed you, that will update the Label in the UserControl XAML as well as the viewmodel property bound to the UserControl's dependency property.
If you want the XAML to set it, you'll need to
Lastly, this:
Margin="0,0,-221,-102"
Is not a good way to do layout. WPF layout with Grid, StackPanel, etc. is much easier and more robust.
I have some problems with a WPF custom control, I'm trying to make it work but just don't get it:
Here is my problem, I'm creating a simple custom control that's almost the same to a TextBox. This control has a dependency property named "Texto", and the binding between the XAML and back-code of the custom control works fine, here is the code:
<UserControl x:Class="WpfCustomControlLibrary1.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"
mc:Ignorable="d"
d:DesignHeight="47" d:DesignWidth="147">
<Grid Height="43" Width="142">
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,8,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Texto}"/>
</Grid>
And the dependency property code:
public static readonly DependencyProperty TextoProperty = DependencyProperty.Register("Texto", typeof(string), typeof(UserControl1));
public string Texto
{
get
{
return (string)GetValue(TextoProperty);
}
set
{
SetValue(TextoProperty, value);
}
}
Ok, now the problem: When I use this control in other windows I try to bind the "Texto" property to a viewmodel (as simple as everything else) but the property on the view model just dont change:
The ViewModel code:
public class ViewModelTest
{
public string SomeText { get; set; }
}
And the code of the applicatoin Window:
public ViewModelTest test;
public Window1()
{
InitializeComponent();
}
private void button1_Click_1(object sender, RoutedEventArgs e)
{
MessageBox.Show(test.SomeText);
MessageBox.Show(uc.Texto);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
test = new ViewModelTest();
this.DataContext = test;
}
And the binding with the property of the view model:
<my:UserControl1 HorizontalAlignment="Left" Margin="27,12,0,0" Name="uc" VerticalAlignment="Top" Texto="{Binding Path=SomeText,Mode=TwoWay}"/>
Just for make it clearer, if I write "Hello" in the custom control and then I push the "button1", the first message shows nothing and the second message shows "Hello".
As you can see I'm fairly new into this, so I hope some of you can help me. Thanks.
Your binding Texto="{Binding SomeText}" works fine, the problem is the rebinding from your user control to the inner textbox. Remember binding will ALWAYS, if not modified, refere to the DataContext. But your DataContext doesn't contain the property Texto. Your control has that, To refere to that you need something called TemplateBinding, but this only works when you are in a ControlTemplate. Which you aren't so what is the solution?
You can use a special form of binding, by changing the source from the DataContext to a control with a given name: First give your UserControl a name
<UserControl x:Class="WpfCustomControlLibrary1.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"
mc:Ignorable="d"
x:Name="mainControl"
d:DesignHeight="47" d:DesignWidth="147">
and now change the binding to refere to the control, not the DataContext of the control anymore.
<TextBox Text="{Binding ElementName=mainControl, Path=Texto}"/>
Now your ViewModel binds to your user control and the content of the user control binds to the user controls Texto property.
Also one minor thing, what you called custom control, is in fact a user control, custom controls are something else.
I'm trying to create a simple Bindable property called MyBoolValue in my UserControl class
First, here the xaml
<UserControl x:Class="TMDE.Controls.SimNaoRadioPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="16"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" Content="Teste" IsChecked="{Binding Path=MyBoolValue}" x:Name="chk" />
</Grid>
</UserControl>
And here the code-behind:
public partial class SimNaoRadioPicker : UserControl
{
public SimNaoRadioPicker()
{
InitializeComponent();
}
public bool? MyBoolValue
{
get
{
return (bool?)GetValue(MyCustomPropertyProperty);
}
set
{
SetValue(MyCustomPropertyProperty, value);
}
}
// Using a DependencyProperty as the backing store for MyCustomProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyCustomPropertyProperty =
DependencyProperty.Register("MyBoolValue",
typeof(bool?), typeof(SimNaoRadioPicker),
new UIPropertyMetadata(MyPropertyChangedHandler));
public static void MyPropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Get instance of current control from sender
// and property value from e.NewValue
// Set public property on TaregtCatalogControl, e.g.
((SimNaoRadioPicker)sender).chk.IsChecked = (bool?)e.NewValue;
}
}
Now, when a try to use this control in another Window, like this:
<my:SimNaoRadioPicker x:Name="test" MyBoolValue="{Binding QCV_Localizacao_Reutilizacao}" Height="16" HorizontalAlignment="Left" Margin="287,456,0,0" VerticalAlignment="Top" Width="167" />
the Binding doesnt working, the property QCV_Localizacao_Reutilizacao doesnt get update and vice-versa.
The DataContext of the Window its a class that implements INotifyPropertyChanged, so the
property "QCV_Localizacao_Reutilizacao" should work ok.
Also if I use a regular CheckBox instead of my UserControl, its works okay
What I'm doing wrong?
I would remove the nullable part of the boolean and just make it a boolean, then set binding modes to two way.
There are two major issues -
First, your binding mode needs to be TwoWay which you can achieve in two ways -
Either specifed it to be TwoWay in xaml like this -
<my:SimNaoRadioPicker MyBoolValue="{Binding QCV_Localizacao_Reutilizacao,
Mode=TwoWay}"/>
The drawback with above apporach is that you have to explicitly set the mode whenever you are using the UserControl's instance.
Another approach would be to modify your DP itself to say that it always be bind by default in a TwoWay mode like this using FrameworkPropertyMetadata -
public static readonly DependencyProperty MyCustomPropertyProperty =
DependencyProperty.Register("MyBoolValue",
typeof(bool?), typeof(SimNaoRadioPicker),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
MyPropertyChangedHandler));
Secondly, QCV_Localizacao_Reutilizacao property lies in your Window's DataContext. But, by default any control will look for binding in its own dataContext so you explicilty need to tell it to look into Window's DataContext using RelativeSource like this -
<my:SimNaoRadioPicker MyBoolValue="{Binding QCV_Localizacao_Reutilizacao,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window}/>