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.
Related
I've got a really simple UserControl I'm trying to create that contains a list of US states. I am trying to expose the selected state via a "SelectedState" property. However, I'm having trouble trying to get this binding to fire once it's hooked up in another UserControl / form.
The XAML for the user control looks like this:
<UserControl x:Class="Sample.Desktop.UserControls.StateDropdown"
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:Sample.Desktop.UserControls"
mc:Ignorable="d"
Width="170" Height="28"
d:DesignHeight="28" d:DesignWidth="170">
<ComboBox x:Name="cboState"
ItemsSource="{Binding StateList, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedValue="{Binding SelectedState, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Abbreviation}"></Label>
<Label> - </Label>
<Label Content="{Binding Name}"></Label>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In the code-behind, I have this code:
public static readonly DependencyProperty SelectedStateProperty = DependencyProperty.Register("SelectedState",
typeof(USState),
typeof(StateDropdown),
new UIPropertyMetadata(null,
new PropertyChangedCallback(OnSelectedStateChanged),
new CoerceValueCallback(OnCoerceSelectedState)));
private static object OnCoerceSelectedState(DependencyObject o, object value)
{
StateDropdown stateDropdown = o as StateDropdown;
if (stateDropdown != null)
return stateDropdown.OnCoerceSelectedState((USState)value);
else
return value;
}
private static void OnSelectedStateChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
StateDropdown stateDropdown = o as StateDropdown;
if (stateDropdown != null)
stateDropdown.OnSelectedStateChanged((USState)e.OldValue, (USState)e.NewValue);
}
protected virtual USState OnCoerceSelectedState(USState value)
{
// TODO: Keep the proposed value within the desired range.
return value;
}
protected virtual void OnSelectedStateChanged(USState oldValue, USState newValue)
{
// TODO: Add your property changed side-effects. Descendants can override as well.
}
public USState SelectedState
{
// IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
get
{
return (USState)GetValue(SelectedStateProperty);
}
set
{
SetValue(SelectedStateProperty, value);
}
}
I wasn't able to get the SelectedValue bound property of SelectedState to fire, so I ended up hooking up the SelectionChanged event.
private void cboState_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems?.Count > 0)
{
SelectedState = (USState)e.AddedItems[0];
}
}
In my other user control, I have this in the XAML:
<uc:StateDropdown Margin="10,0,0,0" SelectedState="{Binding SelectedState}" ></uc:StateDropdown>
And the ViewModel (I'm using Caliburn Micro), I have this property:
protected USState _selectedState;
public USState SelectedState
{
get { return _selectedState; }
set
{
_selectedState = value;
NotifyOfPropertyChange(() => SelectedState);
}
}
The combo is populated as expected. However, SelectedState is never fired/updated when I change the selection.
I had also previously tried using SelectedItem instead of SelectedValue, with the same results.
I'm sure I'm missing something obvious, but I'm having trouble seeing where I went wrong.
EDIT: Here's what fixed the binding.
I removed the SelectionChanged event. Then I modified my "hosting page" usercontrol to set TwoWay binding:
<uc:StateDropdown Margin="10,0,0,0" SelectedState="{Binding SelectedState, Mode=TwoWay}" ></uc:StateDropdown>
As soon as I added that, SelectedState started being updated when I changed the ComboBox value.
The only things I see, is this line :
SelectedValue="{Binding SelectedState, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"
You don't need it, because of the SelectionChanged event. And it can cause the problem.
Also I would bind the SelectedState of the UserControl using a TwoWay binding.
Hope that will help you.
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>
I have a little problem here. I've created custom TreeView using RadTreeView. It all works nice, but I've encountered an obstacle. I've set DependencyProperty for SelectedItem in TreeView. I nest my control in View, bind property to SelectedItem in TwoWay mode, but bound property won't update, it's null all the time, despite DependencyProperty value being set.
Here's tree xaml:
<Grid xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:sdk='http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk'
xmlns:telerik='http://schemas.telerik.com/2008/xaml/presentation' x:Name='this' >
<Grid.Resources>
<DataTemplate x:Key='ChildTemplate'>
<TextBlock Text='{Binding Path=ChildPath}' Margin='5,0' />
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key='NameTemplate' ItemsSource='{Binding ChildrenCollectionPath}' ItemTemplate='{StaticResource ChildTemplate}'>
<TextBlock Text='{Binding Path=ParentPath }' Padding='7'/>
</telerik:HierarchicalDataTemplate>
</Grid.Resources>
<telerik:RadTreeView x:Name='rtvTreeView' Padding='5' BorderThickness='0' IsEditable='False' IsLineEnabled='True' IsExpandOnDblClickEnabled='False' ItemTemplate='{StaticResource NameTemplate}' />
</Grid>
Below is way I nest the control in View:
<windows:TreeViewReuse CollectionSource="{Binding SitesCollectionWithAddress}" ParentPath="Napis" Grid.Column="0" BorderThickness="2" SelectedItemD="{Binding SelectedSide, ElementName=this, UpdateSourceTrigger=Explicit, Mode=TwoWay}" ChildPath="FullAddress" ChildrenCollectionPath="AdresyStrony" BorderBrush="Red" DoubleClickCommand="{Binding TreeViewDoubleClick}">
</windows:TreeViewReuse>
And here's Tree's code behind in parts:
public partial class TreeViewReuse : UserControl
{
static Telerik.Windows.FrameworkPropertyMetadata propertyMetaData = new Telerik.Windows.FrameworkPropertyMetadata(null,
Telerik.Windows.FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(SelectedItemChangedCallback));
public object SelectedItemD
{
get { return GetValue(SelectedItemDProperty); }
set { SetValue(SelectedItemDProperty, value); }
}
public static readonly DependencyProperty SelectedItemDProperty =
DependencyProperty.Register("SelectedItemD", typeof(object), typeof(TreeViewReuse), propertyMetaData);
public TreeViewReuse()
{
InitializeComponent();
Loaded += new RoutedEventHandler(TreeViewReuse_Loaded);
}
void treeView_SelectionChanged(object sender, Telerik.Windows.Controls.SelectionChangedEventArgs e)
{
SelectedItemD = _treeView.SelectedItem;
}
static private void SelectedItemChangedCallback(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
}
Does anyone have an idea why property bound to SelectedItemD does not update? I don't care about setting tree's selected item from it, I only want to set it to selected item.
Here's property:
public StronaSprawy SelectedSide
{
get
{
return _selectedSide;
}
set
{
_selectedSide = value;
}
}
Your Dependency Property looks fine.. all except for that Telerik.Windows.FrameworkPropertyMetadata instance.
Silverlight does not support setting meta data options, so I cant think how the Telerik implementation will achieve that. It is possible that Telerik have their own DP implementation, or even that this type of property meta data only works with their controls.
Try using the standard System.Windows.PropertyMetaData type instead and see if that works for you.
I have a checkable DropDownButton and a Grid.
I want to bind Button's IsChecked parameter with grid's Visibility value.
If (Visibility == Visible) IsCheked = true
I've tried to do like that:
IsChecked="{Binding ElementName=UsersDockWindow, Path=IsVisible}"
but it didn't work, cause IsVisible is readOnly property.
Use the BooleanToVisibilityConverter. Here's an example of how to do the binding using that converter.
Create a VisibilityToBooleanConverter and use that in your binding:
public class VisibilityToBooleanConverter : IValueConverter
{
public object Convert(object value, ...)
{
return (Visibility)value == Visibility.Visible;
}
}
In your XAML:
<Window.Resources>
<!-- assuming the local: xmlns is mapped to the appropriate namespace -->
<local:VisibilityToBooleanConverter x:Key="vbc" />
</Window.Resources>
IsChecked="{Binding Visibility,
ElementName=UsersDockWindow,
Converter={StaticResource vbc}}"
I am using the tree view control in my silverlight project. I use data binding for binding my model to the tree. This works perfect.
After adding some features to the tree I ran into two problems:
When I change a property on my Model, the tree does not get updated, even after my onproperty changed get called and also my converter is not called?(I need to hide the item when a specific property changes) (Answered)
How do I hide an Item in the tree? (I use Textbox + image as Item template)
Stack panel is hidden, but empty container remains in tree
DataTemplate:
<common:HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Visibility="{Binding IsAvailable, Converter={StaticResource ItemVisibleConverter} ,Mode=TwoWay}" >
<Image Source="{Binding Converter={StaticResource ImageConverter}}"/>
<controls:Label Name="myItem" Content="{Binding Description, Converter={StaticResource ItemConverter} ,Mode=TwoWay}" Foreground="Black"/>
</StackPanel>
</common:HierarchicalDataTemplate>
Converter:
public object Convert(object value, Type targetType, object parameter, ystem.Globalization.CultureInfo culture)
{
return GetVisibility(value);
}
private Visibility GetVisibility(object value)
{
bool IsVisible= (bool)value;
if (IsAvailableForDownload)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
Model
public class MyModel: INotifyPropertyChanged
{
public bool IsAvailable
{
get
{
return _IsAvailableForDownload;
}
set
{
_IsAvailableForDownload = value;
onPropertyChanged(this, "IsAvailableForDownload");
}
}
//Code for on property changed event
}
Regards
You probably need to make sure that your model implements INotifyPropertyChanged so that the binding system can do its job.
Could you have a property of type Visibility that your item template binds to, or a bool plus a value converter that returns a Visibility value?
<DataTemplate>
<Grid Visibility="{Binding ThisThingsIsVisible}">
<Button Content="{Binding Blah}" />
</Grid>
</DataTemplate>
I don't know if this is the recommended way or not - could your bound object not expose hidden items in their collections?