WPF - Attached property needs to reference another bound attached property - wpf

I have created an attached property to set the visibility of a UIElement based on a particular enum value. This works fine. However, I need to extend this so that the visibility can be overriden based on the "status" of the sender.
How can I achieve this? I had thought that I could create another attached property which the first attached property could reference, however I need to be able to bind a value to the second attached property rather than just set to an enum value.
EDIT
Below is an example of my problem:
<Window x:Class="AttachedProperty.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:attachedProperty="clr-namespace:AttachedProperty"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<TextBlock Text="Button should be enabled?"/>
<CheckBox IsChecked="{Binding Path=CanClick}"/>
<Button Content="Click Me" IsEnabled="{Binding Path=CanClick}"/>
<Button Content="Manager Only Click" attachedProperty:SecurityBehavior.IsEnabledRole="Mgr"/>
</StackPanel>
</Grid>
The first button's enabled property is just controlled using the checkbox.
The second button's enabled property is controlled by an attachedProperty that determines if you are in the correct security group:
public class SecurityBehavior
{
public static readonly DependencyProperty IsEnabledRoleProperty = DependencyProperty.RegisterAttached(
"IsEnabledRole", typeof (string), typeof (SecurityBehavior), new UIPropertyMetadata(OnIsEnabledRoleChanged));
private static void OnIsEnabledRoleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// In here have some logic that determines if the current user is authorised
// Principal.IsInRole(e.NewValue.ToString() ? true : false;
sender.SetValue(UIElement.IsEnabledProperty, true);
}
public static void SetIsEnabledRole(DependencyObject element, string value)
{
element.SetValue(IsEnabledRoleProperty, value);
}
public static string GetIsEnabledRole(DependencyObject element)
{
return (string) element.GetValue(IsEnabledRoleProperty);
}
}
When run both buttons are enabled - the 1st because the checkbox is checked and the second because I am a manager. When I uncheck the checkbox the 1st button is disabled and I want my attached property to be able to only enable if in the correct security group AND the checkbox is checked.
How can I change by sample so that I can get have the behavior that sets the IsEnabled based on 2 possible inputs?

Not sure why you're after attached properties for this. Based on your requirement you could pull this off with a simple IValueConverter and a Binding for the Visibility of the final control.
So say we have an enum:
public enum MyEnum {
StateOne,
StateTwo
}
and a CheckBox such as:
<CheckBox x:Name="chkBox"
Content="Check Me!!!" />
Now if we want some Button's Visibility to only be visible when enum is StateOne and the checkbox is checked,
we could just have a converter such as:
public class MyConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var checkBoxIsChecked = (bool)value;
var givenEnum = (MyEnum)parameter;
return checkBoxIsChecked && givenEnum == MyEnum.StateOne ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
and in xaml for the Button's:
<StackPanel>
<StackPanel.Resources>
<local:MyConverter x:Key="MyConverter" />
</StackPanel.Resources>
<CheckBox x:Name="chkBox"
Content="Check Me!!!" />
<Button Content="Button One"
Visibility="{Binding ElementName=chkBox,
Path=IsChecked,
Converter={StaticResource MyConverter},
ConverterParameter={x:Static local:MyEnum.StateOne}}" />
<Button Content="Button Two"
Visibility="{Binding ElementName=chkBox,
Path=IsChecked,
Converter={StaticResource MyConverter},
ConverterParameter={x:Static local:MyEnum.StateTwo}}" />
</StackPanel>
With this, "Button One" will become visible when the checkbox is checked, however button two will not as the ConverterParameter passed in for button two is StateTwo.
If it's the IsEnabled state you want to control of the Button, just switch the binding to that property and in the converter just return true or false accordingly instead of Visibility.Visible
Even if you choose to provide the enum value not static and dynamic, you could just make the Binding a MultiBinding and switch the IValueConverter to an IMultiValueConverter
Update:
If for whatever reason you have to go down the route of attached properties, then in the property changed callback of each attached property get the other properties value from the sender and perform your logic accordingly.
public class SecurityBehavior {
public static readonly DependencyProperty IsEnabledRoleProperty = DependencyProperty.RegisterAttached(
"IsEnabledRole",
typeof(string),
typeof(SecurityBehavior),
new UIPropertyMetadata(OnIsEnabledRoleChanged));
public static readonly DependencyProperty IsEnabled2RoleProperty = DependencyProperty.RegisterAttached(
"IsEnabled2Role",
typeof(bool),
typeof(SecurityBehavior),
new UIPropertyMetadata(OnIsEnabled2RoleChanged));
private static void OnIsEnabledRoleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
HandleAttachedPropertyUpdate(sender, (string)e.NewValue, GetIsEnabled2Role(sender));
}
private static void OnIsEnabled2RoleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
HandleAttachedPropertyUpdate(sender, GetIsEnabledRole(sender), (bool)e.NewValue);
}
private static void HandleAttachedPropertyUpdate(DependencyObject sender, string isEnabledRole, bool isEnabled2Role) {
sender.SetValue(UIElement.IsEnabledProperty, isEnabledRole == "Mgr" && isEnabled2Role);
}
public static void SetIsEnabledRole(DependencyObject element, string value) {
element.SetValue(IsEnabledRoleProperty, value);
}
public static string GetIsEnabledRole(DependencyObject element) {
return (string)element.GetValue(IsEnabledRoleProperty);
}
public static void SetIsEnabled2Role(DependencyObject element, bool value) {
element.SetValue(IsEnabled2RoleProperty, value);
}
public static bool GetIsEnabled2Role(DependencyObject element) {
return (bool)element.GetValue(IsEnabled2RoleProperty);
}
}
and xaml:
<StackPanel>
<CheckBox x:Name="chkBox"
Content="Check Me!!!" />
<Button Content="Button One"
local:SecurityBehavior.IsEnabled2Role="{Binding ElementName=chkBox,
Path=IsChecked}"
local:SecurityBehavior.IsEnabledRole="Mgr" />
<Button Content="Button Two"
local:SecurityBehavior.IsEnabled2Role="{Binding ElementName=chkBox,
Path=IsChecked}"
local:SecurityBehavior.IsEnabledRole="NotMgr" />
</StackPanel>

Related

How can I set a Static/Dynamic resource from a binding value?

I want to add dynamic items with a datatemplate that contains a TextBlock control, but the text of the TextBlock control will be selected from a XAML ResourceDictionary. The staticresource name will be obtained based on the result of the binding value.
How can I do that?
I'm trying something like this, but doesn't works.
<DataTemplate x:Key="languageItemTemplate">
<ContentControl>
<StackPanel>
<TextBlock Text="{StaticResource {Binding ResourceName}}"></TextBlock>
<TextBlock Text="{DynamicResource {Binding ResourceName}}"></TextBlock>
</StackPanel>
</ContentControl>
</DataTemplate>
UPDATE
Thanks to Tobias, the fist option of his answer works. But I need to instance the converter first to get it work. Which one is the best idea to do that?
In the application_startup method and use it for all the application or in the Window.Resources of the window I use the converter?
Maybe a merge of both and do that on the Application.Resources?
thanks for your answer.
private void Application_Startup(object sender, StartupEventArgs e)
{
LoadConverters();
}
private void LoadConverters()
{
foreach (var t in System.Reflection.Assembly.GetExecutingAssembly().GetTypes())
{
if (t.GetInterfaces().Any(i => i.Name == "IValueConverter"))
{
Resources.Add(t.Name, Activator.CreateInstance(t));
}
}
}
OR
<local:BindingResourceConverter x:Key="ResourceConverter"/>
<DataTemplate x:Key="languageItemTemplate">
<ContentControl>
<StackPanel>
<TextBlock Text="{Binding Name, Converter={StaticResource ResourceConverter }}" />
</StackPanel>
</ContentControl>
</DataTemplate>
If the resource is an application level resource you could simply use a converter to convert from the resource name to the actual object like this:
public class BindingResourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string resourceKey = value as string;
if (!String.IsNullOrEmpty(resourceKey))
{
var resource = Application.Current.FindResource(resourceKey);
if (resource != null)
{
return resource;
}
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And use it like this:
<TextBlock Text="{Binding ResourceKey, Converter={StaticResource ResourceConverter}}" />
If the resource is in a local scope, we need a reference to the control to search its resources. You can get the resource name and the control by using an attached property:
public class TextBlockHelper
{
public static readonly DependencyProperty TextResourceKeyProperty =
DependencyProperty.RegisterAttached("TextResourceKey", typeof(string),
typeof(TextBlockHelper), new PropertyMetadata(String.Empty, OnTextResourceKeyChanged));
public static string GetTextResourceKey(DependencyObject obj)
{
return (string)obj.GetValue(TextResourceKeyProperty);
}
public static void SetTextResourceKey(DependencyObject obj, string value)
{
obj.SetValue(TextResourceKeyProperty, value);
}
private static void OnTextResourceKeyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
string resourceKey = e.NewValue as string;
if(d is TextBlock tb)
{
var r = tb.TryFindResource(resourceKey);
if (r != null)
{
tb.Text = r.ToString();
}
}
}
}
And you can use it like this:
<Grid>
<Grid.Resources>
<sys:String x:Key="SomeLocalResource">LocalResource</sys:String>
</Grid.Resources>
<TextBlock h:TextBlockHelper.TextResourceKey="{Binding ResourceKey}" />
</Grid>

Updating the value of another property when a DependencyProperty changes

I have a DependencyProperty in my UserControl with a property changed callback. The property works as expected and the callback works as expected.
public double CurrentFlow
{
get { return (double)GetValue(CurrentFlowProperty); }
set { SetValue(CurrentFlowProperty, value); }
}
public static readonly DependencyProperty CurrentFlowProperty = DependencyProperty.Register("CurrentFlow", typeof(double), typeof(MyUserControl), new PropertyMetadata(0.0, OnCurrentFlowPropertyChanged));
private static void OnCurrentFlowPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("CurrentFlow changed.");
}
However, I have a TextBlock in my UserControl where I want to display CurrentFlow as a formatted string. Currently, I have the Text property of the TextBlock binded to CurrentFlow, and it works, but I'm not getting the format I need. (Too many numbers after the decimal.)
<TextBlock Text="{Binding Path=CurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}}" />
Ideally, I'd like to have a property named CurrentFlowString that takes the value from CurrentFlow and formats it to what I want. For example: CurrentFlow.ToString("0.00");
What's the best way to go about this with DependencyProperties? I know how to do this with regular properties but I'm kinda stuck here.
Thanks!
If you want to have more flexibility than using StringFormat, you can also use a custom converter. For example,
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double d)
return $"{d:f2}";
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Then add it to your UserControl.Resources, and use it in your Binding:
<UserControl.Resources>
<local:MyConverter x:Key="MyConverter" />
</UserControl.Resources>
<Grid>
<TextBlock Text="{Binding Path=CurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource MyConverter}}" />
</Grid>
Solution 2:
Based on your comment below, here's an alternative solution. First, create a new dependency property; for example, FormattedCurrentFlow:
public static readonly DependencyProperty FormattedCurrentFlowProperty = DependencyProperty.Register(
"FormattedCurrentFlow", typeof(string), typeof(MyControl), new PropertyMetadata(default(string)));
public string FormattedCurrentFlow
{
get { return (string)GetValue(FormattedCurrentFlowProperty); }
set { SetValue(FormattedCurrentFlowProperty, value); }
}
Since you already have a method to handle changes in CurrentFlow, update the new FormattedCurrentFlow when CurrentFlow changes:
private static void OnCurrentFlowPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var myControl = (MyControl)source;
myControl.FormattedCurrentFlow = $"{myControl.CurrentFlow:f2}";
}
The TextBox in the UserControl can now bind to FormattedCurrentFlow:
<TextBlock Text="{Binding Path=FormattedCurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}}" />

Bind to property of a control in another view

I have set up in a view the height of a control using dockpanels and alignments.
I want to use the computed size of this control as an input for another control in another view.
MainWindow
<StackPanel>
<local:View1 />
<local:View2 />
</StackPanel>
View1
<DockPanel>
...
<Button x:Name="myButton" />
...
</DockPanel>
View2 (where I want to bind the button's height to the first view)
<Button Height="{Binding Path=Button.Height, RelativeSource={RelativeSource AncestorType={x:Type local:View1}}}" />
But it does not work...
I am looking if possible for a xaml-only solution with binding...
You may want to try using dependency properties in order to achieve this. Here is a sample based on your case:
View1:
<DockPanel>
<Button x:Name="myButton" Content="Button in view1" FontSize="32"/>
</DockPanel>
View1 codebehind. Notice that we handle loaded event in order to get the actual height of the button and to assign its value to the DependencyProperty we created:
public static readonly DependencyProperty ButtonHeightProperty = DependencyProperty.Register(
"ButtonHeight", typeof (double), typeof (View1), new PropertyMetadata(default(double)));
public double ButtonHeight
{
get { return (double) GetValue(ButtonHeightProperty); }
set { SetValue(ButtonHeightProperty, value); }
}
public View1()
{
InitializeComponent();
}
private void View1_OnLoaded(object sender, RoutedEventArgs e)
{
ButtonHeight = myButton.ActualHeight;
}
Then in view2 we bind the button height to another dependency property in that user control:
And in view2 codebehind:
public static readonly DependencyProperty ButtonHeightProperty = DependencyProperty.Register(
"ButtonHeight", typeof (double), typeof (View2), new PropertyMetadata(default(double)));
public double ButtonHeight
{
get { return (double) GetValue(ButtonHeightProperty); }
set { SetValue(ButtonHeightProperty, value); }
}
public View2()
{
InitializeComponent();
}
Finally the mainWindow xaml looks like this:
<StackPanel>
<local:View1 x:Name="View1"/>
<local:View2 ButtonHeight="{Binding ElementName=View1,Path=ButtonHeight}"/>
</StackPanel>
And the output:
Hope this helps

User control's Dependency Property doesn't get bound in Silverlight

I have created a user control like numeric updown as follows
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
<TextBox x:Name="InputTextBox" Grid.Row="0" Grid.Column="0" Grid.RowSpan="1"
Style="{StaticResource NumericUpDownTextBoxStyle}"
KeyDown="InputTextBox_KeyDown"
KeyUp="InputTextBox_KeyUp"
GotFocus="InputTextBox_GotFocus"
LostFocus="InputTextBox_LostFocus"
MouseWheel="InputTextBox_MouseWheel"
MouseEnter="InputTextBox_MouseEnter"
LayoutUpdated="InputTextBox_LayoutUpdated"
Text="{Binding Path=ControlValue, Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,NotifyOnValidationError=True}"/>
</StackPanel>
I have bind a ViewModel to this control where I Set ControlValue property to TextBox property of the user control template textbox.
Everthing works fine at a control level. I have exposed from usercontrol.
public static readonly DependencyProperty MaximumValueProperty;
public static readonly DependencyProperty MinimumValueProperty;
public static readonly DependencyProperty StepValueProperty;
public static readonly DependencyProperty TextValueProperty;
My Properties are
public double Maximum
{
get
{
return (double)GetValue(MaximumValueProperty);
}
set
{
SetValue(MaximumValueProperty, value);
this.ViewModel.Maximum = this.Maximum;
}
}
public double Minimum
{
get
{
return (double)GetValue(MinimumValueProperty);
}
set
{
SetValue(MinimumValueProperty, value);
this.ViewModel.Minimum = this.Minimum;
}
}
public double Step
{
get
{
return (double)GetValue(StepValueProperty);
}
set
{
SetValue(StepValueProperty, value);
this.ViewModel.Step = this.Step;
}
}
public double TextValue
{
get
{
return (double)GetValue(TextValueProperty);
}
set
{
SetValue(TextValueProperty, value);
this.ViewModel.ControlValue = Convert.ToString(value);
}
}
Initialization of the property.
static NumericUpDown()
{
MaximumValueProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
MinimumValueProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
StepValueProperty = DependencyProperty.Register("Step", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
TextValueProperty = DependencyProperty.Register("TextValue", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
}
My Usercontrol implementation in the MainPage.xaml page as follows
<local:NumericUpDown Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding ElementName=FranePrice, Path=DataContext.FranePrice}"></local:NumericUpDown>
Where I have another ViewModel which i bind to the XAML page and there is a Property in the ViewModel which i bind to the TextValue property of the Usercontrol.
FramePrice is property in the View model that i bind to the TextValue property of the user control
and Main page XAML is
<UserControl x:Class="DatePicker.MainPage"
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:DatePicker"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Vertical">
<local:NumericUpDown Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding ElementName=FranePrice, Path=DataContext.FranePrice}"></local:NumericUpDown>
<Button Content="Show Date" Height="23" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click"/>
</StackPanel>
</Grid>
This View model of the page where i used user control. On click event i showing TextValue to user.
public class MainPageViewModel : EntityViewModel
{
public MainPageViewModel()
{
}
private double framePrice;
public Double FramePrice
{
get
{
return framePrice;
}
set
{
framePrice = value;
PropertyChangedHandler("FramePrice");
}
}
}
When I change the TextValue in the User control it doesnot change in the FramePrice property of the page viewmodel.
Is anything wrong in the code.???
As per Luke Woodward's post I have updated code as follows
public static readonly DependencyProperty MaximumValueProperty;
public static readonly DependencyProperty MinimumValueProperty;
public static readonly DependencyProperty StepValueProperty;
public static readonly DependencyProperty TextValueProperty;
public static double Max;
public static double Min;
public static double Stp;
public static double Val;
public double Maximum
{
get
{
return (double)GetValue(MaximumValueProperty);
}
set
{
SetValue(MaximumValueProperty, value);
}
}
public double Minimum
{
get
{
return (double)GetValue(MinimumValueProperty);
}
set
{
SetValue(MinimumValueProperty, value);
}
}
public double Step
{
get
{
return (double)GetValue(StepValueProperty);
}
set
{
SetValue(StepValueProperty, value);
}
}
public double TextValue
{
get
{
return (double)GetValue(TextValueProperty);
}
set
{
SetValue(TextValueProperty, value);
}
}
static NumericUpDown()
{
MaximumValueProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onMaximumValueChanged)));
MinimumValueProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onMinimumValueChanged)));
StepValueProperty = DependencyProperty.Register("Step", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onStepValueChanged)));
TextValueProperty = DependencyProperty.Register("TextValue", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onTextValueChanged)));
}
private static void onStepValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Stp = (double)e.NewValue;
}
private static void onMinimumValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Min = (double)e.NewValue;
}
private static void onMaximumValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Max = (double)e.NewValue;
}
private static void onTextValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Val = (double)e.NewValue;
}
Then i accessed Max, Min , Stp and Val property in user control's view model to perform my logic.
and XAML code is follows
<local:NumericUpDown x:Name="ctlUpDown" Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding Path=FramePrice}"></local:NumericUpDown>
and XAML of user control
<StackPanel Margin="5" Orientation="Horizontal" VerticalAlignment="Center">
<TextBox x:Name="InputTextBox" Grid.Row="0" Grid.Column="0" Grid.RowSpan="1"
Height="23" VerticalAlignment="Top"
Width="50" TextAlignment="Center"
KeyDown="InputTextBox_KeyDown"
KeyUp="InputTextBox_KeyUp"
GotFocus="InputTextBox_GotFocus"
LostFocus="InputTextBox_LostFocus"
MouseWheel="InputTextBox_MouseWheel"
MouseEnter="InputTextBox_MouseEnter"
Text="{Binding Path=TextValue, ElementName=ctlUpDown, Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,NotifyOnValidationError=True}"
/>
</StackPanel>
The first thing I noticed wrong about your code was the properties Maximum, Minimum, Step and TextValue. Here's the TextValue property:
public double TextValue
{
get
{
return (double)GetValue(TextValueProperty);
}
set
{
SetValue(TextValueProperty, value);
this.ViewModel.ControlValue = Convert.ToString(value);
}
}
Properties that are backed by a dependency property, such as the four I mentioned above, should ALWAYS look like the following:
public double TextValue
{
get { return (double)GetValue(TextValueProperty); }
set { SetValue(TextValueProperty, value); }
}
In other words, the getter should contain nothing more than a call to GetValue, and the setter should contain nothing more than a call to SetValue.
The reason for this is that when Silverlight changes the value of the TextValue dependency property, it won't do it by using the property above. The values of dependency properties are stored within the Silverlight dependency system, and when Silverlight wants to change the value of one of them, it goes directly to this dependency system. It doesn't call your code at all. Properties like that above are provided only for your convenience, giving you an easy way to access and change the value stored in the dependency property. They will never be called by anything other than your own code.
Generally, if you want a method to be called whenever a dependency property value changes, you need to pass a PropertyChangedCallback in the PropertyMetadata when registering the dependency property. However, I suspect that in your case you won't need to do that.
It seems to me that you have three properties:
the FramePrice property in your view-model class,
the TextValue dependency property of your NumericUpDown user control,
the Text dependency property of the TextBox within your NumericUpDown user control's XAML.
My impression is that you want the FramePrice property in your view-model to always have the same value as the Text property of the TextBox. To do that, you need to bind the FramePrice property to the NumericUpDown's TextValue property, and then bind that to the Text property of the TextBox.
To bind the first two of these properties together, there are a couple of things to change. Firstly, the TextValue property in your <local:NumericUpDown> element should look like
TextValue="{Binding Path=FramePrice}"
The binding {Binding ElementName=FramePrice, Path=DataContext.FramePrice} won't work, because there's no element in your XAML with the attribute x:Name="FramePrice". The value of an ElementName property in a {Binding ...} must match the x:Name of an object in the XAML.
You also need to set up the DataContext for your main page. If your main page view-model object has a zero-argument constructor, one way of doing this is to follow this answer.
To bind the second two properties together, I would:
add an x:Name attribute to the <UserControl> element of your NumericUpDown control (x:Name="ctlUpDown", say),
replace the Text property of the TextBox within your NumericUpDown control with the following:
Text="{Binding Path=TextValue, ElementName=ctlUpDown, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True}"/>
Once you've done that, you can then remove all of the lines this.ViewModel.SomeProperty = ... from your code-behind class. They're not necessary, and as I've already explained they won't be run when you wanted them to.
Finally, is there a reason you're not using the Silverlight Toolkit's NumericUpDown control?
EDIT 2: Against my better judgement I took a look at one of the two Silverlight projects you uploaded (I ignored the one with _2 in it). It bears very little resemblance to your question.
I can only assume you want the two textboxes (one of which is in a user control) to always have the same value. I was able to do this after making the following changes:
MainPageViewModel.cs: add ClearErrorFromProperty("DPropertyBind"); to the property setter. (Otherwise the validation error never gets cleared.)
MyUserControlWVM.xaml: removed reference to LostFocus event handler, added binding on Text property and added add x:Name attribute to the <UserControl> element. In other words, it now looks like the following:
<UserControl x:Class="DependencyPropertyBinding.MyUserControlWVM"
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"
mc:Ignorable="d"
x:Name="ctlWVM"
d:DesignHeight="300" d:DesignWidth="205">
<StackPanel Orientation="Horizontal" Width="204" Height="32">
<TextBox x:Name="textbox" Height="30" Width="200" Text="{Binding Path=DProperty, ElementName=ctlWVM, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
</StackPanel>
</UserControl>
MyUserControlWVM.xaml.cs: renamed dependency property DependencyPropertyValue to DPropertyProperty (the naming convention is that the static readonly field has the name of the property (in this case DProperty) with Property appended). I also removed the TextBox_LostFocus event handler.
If the code above is accurate you have spelt FramePrice as FranePrice in the binding
The output window should have shown this as a binding error when the page loaded.
it is currently
Binding ElementName=FranePrice, Path=DataContext.FranePrice
should be:
Binding ElementName=FramePrice, Path=DataContext.FramePrice
"With great binding capabilities comes great responsibility" :)

Databind radiobutton group to property

I have a radiobutton group:
<TextBlock Height="24" Text="Update Interval (min):"/>
<RadioButton x:Name="radioButtonTimerNone" IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, Mode=TwoWay}"} Content="None" />
<RadioButton x:Name="radioButtonTimerOne" IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, Mode=TwoWay}"
Content="1" />
<RadioButton x:Name="radioButtonTimerFive" IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, Mode=TwoWay}"
Content="5" />
And a property:
public int UpdateInterval {
get { return _updateInterval; }
set { _updateInterval = value;
onPropertyChanged("UpdateInterval");
}
}
How do I bind the radiobuttons to the property, so radioButtonTimerNone is checked when UpdateInterval is 0, radioButtonTimerOne is checked when UpdateInterval is 1, etc.
I have tried to create a converter, but it doesn't identify which rb is being set:
[ValueConversion(typeof(RadioButton), typeof(bool))]
class UpdateIntervalToCheckedConverter : System.Windows.Data.IValueConverter
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
I expected 'value' to be a radiobutton, but it appears to be the value of UpdateInterval.
Thanks for any hints...
If you are using MVVM and are bound to a ViewModel (I would guess that you are), I usually consider my ViewModel to be a big ValueConverter. Why not put that logic into properties for each?
Here's an example of one of them:
public bool Timer5Enabled
{
get { return UpdateInterval == 5; }
}
And then you'd just bind to that:
<RadioButton
x:Name="radioButtonTimerOne"
IsChecked="{Binding Timer5Enabled, Mode=OneWay}"
Content="1" />
The only thing you'd need to change would be to tie your interval update logic to raise OnChanged for your dependent properties:
public int UpdateInterval {
get { return _updateInterval; }
set { _updateInterval = value;
onPropertyChanged("UpdateInterval");
onPropertyChanged("Timer5Enabled");
onPropertyChanged("...");
}
}
ValueConverters are good to avoid if you can.
Your value converter doesn't get told which RadioButton changed the value - all the binding knows is that the "IsChecked" property was changed, so the new value for IsChecked is the only thing it can tell the converter.
The first thing that springs to my mind is to supply a converter parameter with each of your bindings:
<RadioButton
x:Name="radioButtonTimerNone"
IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, ConverterParameter=0, Mode=TwoWay}"
Content="None" />
<RadioButton
x:Name="radioButtonTimerOne"
IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, ConverterParameter=1, Mode=TwoWay}"
Content="1" />
<RadioButton
x:Name="radioButtonTimerFive"
IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, ConverterParameter=5, Mode=TwoWay}"
Content="5" />
So now the "parameter" parameter on the "Convert" method will have the value "0", "1" or "5" depending on which RadioButton was checked. I think, though I'm not certain, that the parameter will be of type string, so you may have to take that into account when interrogating the value.
You could probably consider having a List<...> with different intervals. The list type should preferably be some custom type (e.g. UpdateInterval) or a KeyValuePair<K,V> to decouple what is shown to the user from what is an actual value.
Then you could have a ListBox bound to this list, and each ListBoxItem templated to show a RadioButton. Then, in the template, you bind radio button's IsChecked to ListBoxItem.IsSelected.
The final touch is just to bind your property to the ListBox's SelectedItem or SelectedValue.
This has been a very annoying problem for me.
The WPF RadioButton class has a bug that removes the binding for the IsChecked property when you assign multiple RadioButtons to the same GroupName. People have given many work arounds, but when I had to visit this again I came up with one that doesn't make me queasy.
So what you have to do is subclass the RadioButton class with this:
public class RadioButton: System.Windows.Controls.RadioButton
{
protected override void OnClick()
{
base.OnClick();
SetValue(CurrentValueProperty,CheckedValue);
}
public int CurrentValue
{
get { return (int)GetValue(CurrentValueProperty); }
set { SetValue(CurrentValueProperty, value); }
}
// Using a DependencyProperty as the backing store for CurrentValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentValueProperty =
DependencyProperty.Register("CurrentValue", typeof(int), typeof(RadioButton), new FrameworkPropertyMetadata(0,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CurrentValue_Changed));
public static void CurrentValue_Changed(object sender, DependencyPropertyChangedEventArgs e)
{
((RadioButton)sender).IsChecked = ((RadioButton)sender).CurrentValue == ((RadioButton)sender).CheckedValue;
}
public int CheckedValue
{
get { return (int)GetValue(CheckedValueProperty); }
set { SetValue(CheckedValueProperty, value); }
}
// Using a DependencyProperty as the backing store for CheckedValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CheckedValueProperty =
DependencyProperty.Register("CheckedValue", typeof(int), typeof(RadioButton), new UIPropertyMetadata(0));
}
All this does is add two dependency properties so you can do an equality comparison.
So in XAML an example would be this:
<UserControl x:Class="MyClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:MVVMLibrary;assembly=MVVMLibrary"
Height="Auto" Width="Auto">
<c:RadioButton CurrentValue="{Binding MyValue}" CheckedValue="1" GroupName="SearchType" >Value 1</c:RadioButton>
<c:RadioButton Grid.Column="1" CurrentValue="{Binding MyValue}" CheckedValue="2" GroupName="SearchType" >Value 2</c:RadioButton>
</UserControl>
So If you notice all you do is
1) Bind to the CurrentValue property
2) Set the CheckedValue property to the value that would make the RadioButton checked
3) Set the RadioButtons to the same GroupName
If you notice i made CurrentValue and CheckedValue int type. The reason I did this is so that you could actually bind CurrentValue to an enumeration. I think this is awesome, but that's just my opinion which probably doesn't count for much. :)
Hope this helps somebody.
One possibility is to implement an IValueConverter and configure it passing the expected enumeration value for every RadioButton you need to bind, as I described here in my blog.

Resources