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}/>
Related
I have the following scenario and hierarchy of XAML elements in my page:
<Page> ....
<StackPanel> ...
<Grid> ....
<StackPanel>
<uc:MyUserControl
ReferencedButton={Binding ElementName=RightButton} />
<Button x:Name="RightButton" Click="{x:Bind ViewModel.OpenFlyout}" Content="Clickme" />
</StackPanel>
......
Then the code behind from 'MyUserControl'
public UIElement ReferencedButton
{
get { return (UIElement)GetValue(ReferencedButtonProperty); }
set { SetValue(ReferencedButtonProperty, value); }
}
public static readonly DependencyProperty ReferencedButtonProperty =
DependencyProperty.Register(nameof(ReferencedButton), typeof(UIElement), typeof(MyUserControl), null);
So far so good, however I was expecting that in my code behind, the 'ReferencedButton' property would be filled with a reference to the 'RightButton' button. However it always returns null.
I even tried:
{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, ElementName=RightButton}
I know it is possible to bind the element, because I got the example from a DevExpress component, but still without any success.
I am following the suggestions/rules from the following docs:
Binding ElementName
XAML Namescopes
p.s: I know that I can pass the reference to the button in my code behind however I would like to do this through XAML itself.
It turns out that I needed to use a PropertyChangedCallback to make it work. So the solution is as below:
public static readonly DependencyProperty ReferencedButtonProperty=
DependencyProperty.Register(nameof(ReferencedButton),
typeof(UIElement),
typeof(MyUserControl),
new PropertyMetadata(default(UIElement),
new PropertyChangedCallback(PlacementCallBack)));
and in the code behind of my control I can access and set the value by implementing the PlacementCallBack like this:
public static void PlacementCallBack(object sender, DependencyPropertyChangedEventArgs e)
{
var myuserControl = sender as MyUserControl;
myuserControl.ReferencedButton = e.NewValue as UIElement;
}
The object DependencyPropertyChangedEventArgs contains two propeties NewValue and OldValue, they hold the old and new values of the previous object set.
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've got an interesting UI problem. I've got a RadBusyIndicator from Telerik wrapped inside of a UserControl (for ease of switching to the Windows busy indicator if the Telerik one still has a memory leak). When I put content into the control, if it has anything more than a ContentControl in between the opening and closing tags of the wrapper control, everything with an x:Name attribute is null in the code behind and causes an exception when the page is loaded.
Here is a likeness of the code with names removed to protect the innocent.
The xaml...
<UserControl>
<Grid x:Name="Indicator">
<telerik:RadBusyIndicator x:Name="BusyIndicator" IsBusy="{Binding Path=IsStatusBusy, Mode=TwoWay}" BusyContent="{Binding Path=WaitingContent, Mode=TwoWay}" Content="{Binding Path=UserContent, Mode=TwoWay}"/>
</Grid>
</UserControl>
And the code behind...
[ContentProperty("UserContent")]
public partial class CustomBusyIndicator : UserControl
{
public CustomBusyIndicator()
{
InitializeComponent();
Indicator.DataContext = this;
}
public UIElement UserContent
{
get { return (UIElement)GetValue(UserContentProperty); }
set { SetValue(UserContentProperty, value); }
}
private static readonly DependencyProperty UserContentProperty = DependencyProperty.Register("PageContent",
typeof(UIElement), typeof(CustomBusyIndicator), new PropertyMetadata(null));
private static readonly DependencyProperty WaitingContentProperty = DependencyProperty.Register("WaitingContent",
typeof (object), typeof (CustomBusyIndicator), new PropertyMetadata(null, OnWaitingContentChanged));
private static void OnWaitingContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{}
private static readonly DependencyProperty IsStatusBusyProperty = DependencyProperty.Register("IsStatusBusy",
typeof (bool), typeof (CustomBusyIndicator), new PropertyMetadata(false, OnIsStatusBusyChanged));
private static void OnIsStatusBusyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{}
public bool IsStatusBusy
{
get { return (bool) GetValue(IsStatusBusyProperty); }
set { SetValue(IsStatusBusyProperty, value); }
}
public object WaitingContent
{
get { return GetValue(WaitingContentProperty); }
set { SetValue(WaitingContentProperty, value); }
}
}
And I'm using it like this.....
<CustomBusyIndicator IsStatus={Binding IsBusy}>
<CustomBusyIndicator.WaitingContent>
<TextBlock Text="Loading..." Foreground="Black" />
</CustomBusyIndicator.WaitingContent>
<Grid>
.
.
.
.
</Grid>
</CustomBusyIndicator>
Any ideas? Thanks in advance for your help!
Edit
I've now established that it is the x:Name that seems to be causing the issues. They are null in the code behind after InitializeComponent() is called.
You are deriving from UserControl, so what happens... let's see:
Your class inherits a public property called Content and exactly this property is the dedicated ContentProperty of the baseclass, caused by an annotation like [ContentProperty("Content")] at the baseclass level.
That's the reason why normaly everything you declare in the xaml-part of you userControl definition is showing up when loaded.
So when you see this...
<UserControl ... >
<Grid x:Name="Indicator">
<telerik:RadBusyIndicator x:Name="BusyIndicator" ... />
</Grid>
</UserControl>
it is technically the same as writing this:
<UserControl ... >
<UserControl.Content>
<Grid x:Name="Indicator">
<telerik:RadBusyIndicator x:Name="BusyIndicator" ... />
</Grid>
</UserControl.Content>
</UserControl>
That means whenever you use your UserControl somewhere in your xaml and add content the way you did...
<CustomBusyIndicator ...>
<Grid> ... </Grid>
</CustomBusyIndicator>
...you are overwriting everything that was declared inside the xaml-part of the UserControl definition (and it does not matter that you annotated another property to be the ContentProperty, this just means you set the new ContentProperty twice).
So what are your options now:
Option Number 1: Keep UserControl as your base, but use your property UserContent only explicitly
so the usage would look like this:
<CustomBusyIndicator ...>
<CustomBusyIndicator.UserContent>
<Grid> ... </Grid>
</CustomBusyIndicator.UserContent>
</CustomBusyIndicator>
Option Number 2: derive from Control or ContentControl and transform your UserControl's xaml-part into a default ControlTemplate
that way you can use it like this
<CustomBusyIndicator ...>
<Grid> ... </Grid>
</CustomBusyIndicator>
but you have to make sure your template is found when the xaml is parsed. I usually do the following:
create a ResourceDictionary CustomBusyIndicator.xaml
add an entry to themes/generic.xaml that includes the dictionary
add DefaultStyleKey = typeof(CustomBusyIndicator); to your control's constructor
add an implicit style to CustomBusyIndicator.xaml
And this has another ramification. You cannot use named elements as easily as before: you have to write an override for OnApplyTemplate and get references to those named elements via GetTemplateChild("BusyIndicator") as RadBusyIndicator;
Option Number 3: Keep UserControl as your base, and UserContent as the ContentProperty, but set the xaml-part explicitly
so the definition would look like this:
<UserControl ... >
<UserControl.Content>
<Grid x:Name="Indicator">
<telerik:RadBusyIndicator x:Name="BusyIndicator" ... />
</Grid>
</UserControl.Content>
</UserControl>
I'm playing around with WPF Binding and variables. Apparently one can only bind DependencyProperties. I have come up with the following, which works perfectly fine:
The code-behind file:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string Test
{
get { return (string)this.GetValue(TestProperty); }
set { this.SetValue(TestProperty, value); }
//set { this.SetValue(TestProperty, "BBB"); }
}
public static readonly DependencyProperty TestProperty = DependencyProperty.Register(
"Test", typeof(string), typeof(MainWindow), new PropertyMetadata("CCC"));
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Test);
Test = "AAA";
MessageBox.Show(Test);
}
}
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBox Height="31" HorizontalAlignment="Left" Margin="84,86,0,0" Name="textBox1" VerticalAlignment="Top" Width="152"
Text="{Binding Test, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="320,85,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBox Height="31" HorizontalAlignment="Left" Margin="84,138,0,0" Name="textBox2" Text="{Binding Test, Mode=TwoWay}" VerticalAlignment="Top" Width="152" />
</Grid>
The two TextBoxes update one an other. And the Button sets them to "AAA".
But now I replaced the Setter function with the one that is commented out (simulating some manipulation of the given value). I would expect that whenever the property value is changed it will be reset to "BBB". It does so when you press the button, that is when you set the property in code. But it does for some reason not affect the WPF Bindings, that is you can change the TextBox contents and thus the property, but apparently the Setter is never called.
I wonder why that is so, and how one would go about to achive the expected behaviour.
The CLR Property wrapper for a Dependency Property is never guaranteed to be called and therefore, you should never place any additional logic there. Whenever you need additional logic when a DP is changed, you should use the property changed callback.
In your case..
public string Test
{
get { return (string)this.GetValue(TestProperty); }
set { this.SetValue(TestProperty, value); }
}
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(string),
typeof(MainWindow),
new PropertyMetadata("CCC", TestPropertyChanged));
private static void TestPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MainWindow mainWindow = source as MainWindow;
string newValue = e.NewValue as string;
// Do additional logic
}
Your change will not affect the binding because the XAML will call SetValue directly, instead of calling your property setter.That is how the dependency property system works.When a dependency property is registered a default value can be specified.This value is returned from GetValue and is the default value for your property.
Check the link below and read through to Robert Rossney's post to get a fair overview
WPF: What distinguishes a Dependency Property from a regular CLR Property?
also don't miss
http://msdn.microsoft.com/en-us/library/ms753358.aspx
and
http://msdn.microsoft.com/en-us/library/ms752914.aspx
Also note that unlike in normal CLR properties any custom logic you write in the setter will not be executed in Dependency Properties,instead you have to use the PropertyChangedCallback mechanism
http://blogs.msdn.com/b/delay/archive/2010/03/23/do-one-thing-and-do-it-well-tip-the-clr-wrapper-for-a-dependencyproperty-should-do-its-job-and-nothing-more.aspx
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.