Here is my firt question for Stackoverflow, I hope that will be ok!
I'm working on a custom Dropdown Button in WPF, and I would like to add a click event on the buttons "Text1" and "Text2". I have to put this dropdown button in a DLL so I use the WPF CustomControl library. So in the perfect world, I would like to create several methods in the MainWindow.xaml.cs and send the name of the method in a class where the name of the button, the icon , the tooltip, ... that will be used in the generic.xaml to find the method to call.
I hope what I said is clear :3
The purpose of this is to have a reusable dropdown button where I can add some click event in the items when we click on it.
Here is the generic.xaml with my dropdown button :
<Style TargetType="{x:Type local:ButtonDropdown}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ButtonDropdown}">
<mah:DropDownButton Content="{Binding Path=Text, RelativeSource={RelativeSource TemplatedParent}}"
ToolTip="{Binding Path=ToolTip, RelativeSource={RelativeSource TemplatedParent}}"
x:Name="DropDownButton"
Orientation="Vertical"
BorderThickness="0"
ItemsSource="{Binding ItemsSource}">
<mah:DropDownButton.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0" ToolTip="{Binding Tooltip}">
<StackPanel.InputBindings>
<MouseBinding Command="{Binding Path=SomeCommand, RelativeSource={RelativeSource TemplatedParent}}" MouseAction="LeftClick" />
</StackPanel.InputBindings>
<Image Source="{Binding Icon}" Width="16"></Image>
<TextBlock Text="{Binding Text}" x:Name="PART_DropdownButton">
</TextBlock>
</StackPanel>
</DataTemplate>
</mah:DropDownButton.ItemTemplate>
<mah:DropDownButton.Icon>
<Image Source="{Binding Path=Icon, RelativeSource={RelativeSource TemplatedParent}}" Width="32"></Image>
</mah:DropDownButton.Icon>
</mah:DropDownButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The call of this custom dropdown in the MainWindow.xaml :
<CustomButton:ButtonDropdown Text="Dropdown"
x:Name="ButtonDropdown"
Icon="Images/Open.png"
ToolTip="TOOLTIP DROPDOWN"
ItemsSource="{Binding Items}"/>
Here is my method OnApplyTemplate I add the line 'TextBlock textblock= GetTemplateChild("PART_DropdownButton") as TextBlock;' after the first answer.
public override void OnApplyTemplate()
{
DropDownButton dropDownButton = GetTemplateChild("DropDownButton") as DropDownButton;
TextBlock textblock= GetTemplateChild("PART_DropdownButton") as TextBlock;
textblock.MouseDown += Method1;
dropDownButton.ItemsSource = DropdownItems;
dropDownButton.Click += ButtonDropdown_Click;
}
And finally the class I have created for items in the dropdown :
public class DropdownItem
{
private string text;
private string icon;
private string tooltip;
private string clickEvent;
}
For the moment I have try with command and mousedown on textblock but don't work :/
Edit : I add the name for the textBlock and I add my method OnApplyTemplate from my ButtonDropdown.cs. The dropDownButton.click is ok but when I try to get the "PART_DropdownButton" that is null. I think because of there is not only one but several textBlock so he don't know which one to take. But that is my problem how to asign a different method on all textblock.mouseDown ? How can we put a different name on all textblock ?
Assuming your Dropdown Button derives from a button control give the DropDown button a name in the xaml file e.g. "PART_DropdownButton". Then reference the name in the code behind in the OnApplyTemplate procedure. Here you can add an event handler trapping your mouse events.
private DropdownButton dropdownbutton = null;
...
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
dropdownbutton = base.GetTemplateChild("PART_DropdownButton") as ToggleButton;
if (dropdownbutton != null)
{
dropdownbutton.MouseDown += MouseDown_Click;
}
else
....;
}
Next write your event handler for MouseDown_Click.
Regards Martin
I finally find something that works like I want !
I add an Icommand in my dropdownItem. That will contain my method.
public class DropdownItem
{
private string text;
private string icon;
private string tooltip;
private string clickEvent;
public ICommand ClickCommand { get; set; }
}
In my MainWindow.xaml.cs I add the command I need.
private ICommand _command1;
private ICommand _command2;
public MainWindow()
{
InitializeComponent();
Items.Add(new DropdownItem("Text1", "Images/Open.png", "Method1", "TEST")
{
ClickCommand = Command1
});
Items.Add(new DropdownItem("Text2", "Images/Open.png", "method2", "TEST2")
{
ClickCommand = Command2
});
ButtonDropdown.DropdownItems = Items;
}
public ICommand Command1
{
get
{
return _command1 = new RelayCommand(Method1);
}
}
public ICommand Command2
{
get
{
return _command2 = new RelayCommand(Method2);
}
}
public void Method1()
{
MessageBox.Show("Method 1");
}
public void Method2()
{
MessageBox.Show("Method 2");
}
And finally I add the call to this method in my generic.xaml
<MouseBinding Command="{Binding ClickCommand}" MouseAction="LeftClick" />
Thanks for your help, that's because of your comments and answers that I understood that I was looking in the bad direction
EDIT: Problem was fixed in .NET 4.0.
I have been trying to bind a group of radio buttons to a view model using the IsChecked button. After reviewing other posts, it appears that the IsChecked property simply doesn't work. I have put together a short demo that reproduces the problem, which I have included below.
Here is my question: Is there a straightforward and reliable way to bind radio buttons using MVVM? Thanks.
Additional information: The IsChecked property doesn't work for two reasons:
When a button is selected, the IsChecked properties of other buttons in the group don't get set to false.
When a button is selected, its own IsChecked property does not get set after the first time the button is selected. I am guessing that the binding is getting trashed by WPF on the first click.
Demo project: Here is the code and markup for a simple demo that reproduces the problem. Create a WPF project and replace the markup in Window1.xaml with the following:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<StackPanel>
<RadioButton Content="Button A" IsChecked="{Binding Path=ButtonAIsChecked, Mode=TwoWay}" />
<RadioButton Content="Button B" IsChecked="{Binding Path=ButtonBIsChecked, Mode=TwoWay}" />
</StackPanel>
</Window>
Replace the code in Window1.xaml.cs with the following code (a hack), which sets the view model:
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new Window1ViewModel();
}
}
}
Now add the following code to the project as Window1ViewModel.cs:
using System.Windows;
namespace WpfApplication1
{
public class Window1ViewModel
{
private bool p_ButtonAIsChecked;
/// <summary>
/// Summary
/// </summary>
public bool ButtonAIsChecked
{
get { return p_ButtonAIsChecked; }
set
{
p_ButtonAIsChecked = value;
MessageBox.Show(string.Format("Button A is checked: {0}", value));
}
}
private bool p_ButtonBIsChecked;
/// <summary>
/// Summary
/// </summary>
public bool ButtonBIsChecked
{
get { return p_ButtonBIsChecked; }
set
{
p_ButtonBIsChecked = value;
MessageBox.Show(string.Format("Button B is checked: {0}", value));
}
}
}
}
To reproduce the problem, run the app and click Button A. A message box will appear, saying that Button A's IsChecked property has been set to true. Now select Button B. Another message box will appear, saying that Button B's IsChecked property has been set to true, but there is no message box indicating that Button A's IsChecked property has been set to false--the property hasn't been changed.
Now click Button A again. The button will be selected in the window, but no message box will appear--the IsChecked property has not been changed. Finally, click on Button B again--same result. The IsChecked property is not updated at all for either button after the button is first clicked.
If you start with Jason's suggestion then the problem becomes a single bound selection from a list which translates very nicely to a ListBox. At that point it's trivial to apply styling to a ListBox control so that it shows up as a RadioButton list.
<ListBox ItemsSource="{Binding ...}" SelectedItem="{Binding ...}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton Content="{TemplateBinding Content}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Looks like they fixed binding to the IsChecked property in .NET 4. A project that was broken in VS2008 works in VS2010.
For the benefit of anyone researching this question down the road, here is the solution I ultimately implemented. It builds on John Bowen's answer, which I selected as the best solution to the problem.
First, I created a style for a transparent list box containing radio buttons as items. Then, I created the buttons to go in the list box--my buttons are fixed, rather than read into the app as data, so I hard-coded them into the markup.
I use an enum called ListButtons in the view model to represent the buttons in the list box, and I use each button's Tag property to pass a string value of the enum value to use for that button. The ListBox.SelectedValuePath property allows me to specify the Tag property as the source for the selected value, which I bind to the view model using the SelectedValue property. I thought I would need a value converter to convert between the string and its enum value, but WPF's built-in converters handled the conversion without problem.
Here is the complete markup for Window1.xaml:
<Window x:Class="RadioButtonMvvmDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<!-- Resources -->
<Window.Resources>
<Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border BorderThickness="0" Background="Transparent">
<RadioButton
Focusable="False"
IsHitTestVisible="False"
IsChecked="{TemplateBinding IsSelected}">
<ContentPresenter />
</RadioButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border BorderThickness="0" Padding="0" BorderBrush="Transparent" Background="Transparent" Name="Bd" SnapsToDevicePixels="True">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<!-- Layout -->
<Grid>
<!-- Note that we use SelectedValue, instead of SelectedItem. This allows us
to specify the property to take the value from, using SelectedValuePath. -->
<ListBox Style="{StaticResource RadioButtonList}" SelectedValuePath="Tag" SelectedValue="{Binding Path=SelectedButton}">
<ListBoxItem Tag="ButtonA">Button A</ListBoxItem>
<ListBoxItem Tag="ButtonB">Button B</ListBoxItem>
</ListBox>
</Grid>
</Window>
The view model has a single property, SelectedButton, which uses a ListButtons enum to show which button is selected. The property calls an event in the base class I use for view models, which raises the PropertyChanged event:
namespace RadioButtonMvvmDemo
{
public enum ListButtons {ButtonA, ButtonB}
public class Window1ViewModel : ViewModelBase
{
private ListButtons p_SelectedButton;
public Window1ViewModel()
{
SelectedButton = ListButtons.ButtonB;
}
/// <summary>
/// The button selected by the user.
/// </summary>
public ListButtons SelectedButton
{
get { return p_SelectedButton; }
set
{
p_SelectedButton = value;
base.RaisePropertyChangedEvent("SelectedButton");
}
}
}
}
In my production app, the SelectedButton setter will call a service class method that will take the action required when a button is selected.
And to be complete, here is the base class:
using System.ComponentModel;
namespace RadioButtonMvvmDemo
{
public abstract class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Protected Methods
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the changed property.</param>
protected void RaisePropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, e);
}
}
#endregion
}
}
Hope that helps!
One solution is to update the ViewModel for the radio buttons in the setter of the properties. When Button A is set to True, set Button B to false.
Another important factor when binding to an object in the DataContext is that the object should implement INotifyPropertyChanged. When any bound property changes, the event should be fired and include the name of the changed property. (Null check omitted in the sample for brevity.)
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool _ButtonAChecked = true;
public bool ButtonAChecked
{
get { return _ButtonAChecked; }
set
{
_ButtonAChecked = value;
PropertyChanged(this, new PropertyChangedEventArgs("ButtonAChecked"));
if (value) ButtonBChecked = false;
}
}
protected bool _ButtonBChecked;
public bool ButtonBChecked
{
get { return _ButtonBChecked; }
set
{
_ButtonBChecked = value;
PropertyChanged(this, new PropertyChangedEventArgs("ButtonBChecked"));
if (value) ButtonAChecked = false;
}
}
}
Edit:
The issue is that when first clicking on Button B the IsChecked value changes and the binding feeds through, but Button A does not feed through its unchecked state to the ButtonAChecked property. By manually updating in code the ButtonAChecked property setter will get called the next time Button A is clicked.
Here is another way you can do it
VIEW:
<StackPanel Margin="90,328,965,389" Orientation="Horizontal">
<RadioButton Content="Mr" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
<RadioButton Content="Mrs" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
<RadioButton Content="Ms" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
<RadioButton Content="Other" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}}" GroupName="Title"/>
<TextBlock Text="{Binding SelectedTitle, Mode=TwoWay}"/>
</StackPanel>
ViewModel:
private string selectedTitle;
public string SelectedTitle
{
get { return selectedTitle; }
set
{
SetProperty(ref selectedTitle, value);
}
}
public RelayCommand TitleCommand
{
get
{
return new RelayCommand((p) =>
{
selectedTitle = (string)p;
});
}
}
Not sure about any IsChecked bugs, one possible refactor you could make to your viewmodel:the view has a number of mutually exclusive states represented by a series of RadioButtons, only one of which at any given time can be selected. In the view model, just have 1 property (e.g. an enum) which represents the possible states: stateA, stateB, etc That way you wouldn't need all the individual ButtonAIsChecked, etc
A small extension to John Bowen's answer: It doesn't work when the values don't implement ToString(). What you need instead of setting the Content of the RadioButton to a TemplateBinding, just put a ContentPresenter in it, like this:
<ListBox ItemsSource="{Binding ...}" SelectedItem="{Binding ...}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}">
<ContentPresenter/>
</RadioButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
This way you can additionally use DisplayMemberPath or an ItemTemplate as appropriate. The RadioButton just "wraps" the items, providing the selection.
I know this is an old question and the original issue was resolved in .NET 4. and in all honesty this is slightly off topic.
In most cases where I've wanted to use RadioButtons in MVVM it's to select between elements of an enum, this requires binding a bool property in the VM space to each button and using them to set an overall enum property that reflects the actual selection, this gets very tedious very quick. So I came up with a solution that is re-usable and very easy to implement, and does not require ValueConverters.
The View is pretty much the same, but once you have your enum in place the VM side can be done with a single property.
MainWindowVM
using System.ComponentModel;
namespace EnumSelectorTest
{
public class MainWindowVM : INotifyPropertyChanged
{
public EnumSelectorVM Selector { get; set; }
private string _colorName;
public string ColorName
{
get { return _colorName; }
set
{
if (_colorName == value) return;
_colorName = value;
RaisePropertyChanged("ColorName");
}
}
public MainWindowVM()
{
Selector = new EnumSelectorVM
(
typeof(MyColors),
MyColors.Red,
false,
val => ColorName = "The color is " + ((MyColors)val).ToString()
);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The class that does all the work inherits from DynamicObject. Viewed from the outside it creates a bool property for each element in the enum prefixed with 'Is', 'IsRed', 'IsBlue' etc. that can be bound to from XAML. Along with a Value property that holds the actual enum value.
public enum MyColors
{
Red,
Magenta,
Green,
Cyan,
Blue,
Yellow
}
EnumSelectorVM
using System;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
namespace EnumSelectorTest
{
public class EnumSelectorVM : DynamicObject, INotifyPropertyChanged
{
//------------------------------------------------------------------------------------------------------------------------------------------
#region Fields
private readonly Action<object> _action;
private readonly Type _enumType;
private readonly string[] _enumNames;
private readonly bool _notifyAll;
#endregion Fields
//------------------------------------------------------------------------------------------------------------------------------------------
#region Properties
private object _value;
public object Value
{
get { return _value; }
set
{
if (_value == value) return;
_value = value;
RaisePropertyChanged("Value");
_action?.Invoke(_value);
}
}
#endregion Properties
//------------------------------------------------------------------------------------------------------------------------------------------
#region Constructor
public EnumSelectorVM(Type enumType, object initialValue, bool notifyAll = false, Action<object> action = null)
{
if (!enumType.IsEnum)
throw new ArgumentException("enumType must be of Type: Enum");
_enumType = enumType;
_enumNames = enumType.GetEnumNames();
_notifyAll = notifyAll;
_action = action;
//do last so notification fires and action is executed
Value = initialValue;
}
#endregion Constructor
//------------------------------------------------------------------------------------------------------------------------------------------
#region Methods
//---------------------------------------------------------------------
#region Public Methods
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string elementName;
if (!TryGetEnumElemntName(binder.Name, out elementName))
{
result = null;
return false;
}
try
{
result = Value.Equals(Enum.Parse(_enumType, elementName));
}
catch (Exception ex) when (ex is ArgumentNullException || ex is ArgumentException || ex is OverflowException)
{
result = null;
return false;
}
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object newValue)
{
if (!(newValue is bool))
return false;
string elementName;
if (!TryGetEnumElemntName(binder.Name, out elementName))
return false;
try
{
if((bool) newValue)
Value = Enum.Parse(_enumType, elementName);
}
catch (Exception ex) when (ex is ArgumentNullException || ex is ArgumentException || ex is OverflowException)
{
return false;
}
if (_notifyAll)
foreach (var name in _enumNames)
RaisePropertyChanged("Is" + name);
else
RaisePropertyChanged("Is" + elementName);
return true;
}
#endregion Public Methods
//---------------------------------------------------------------------
#region Private Methods
private bool TryGetEnumElemntName(string bindingName, out string elementName)
{
elementName = "";
if (bindingName.IndexOf("Is", StringComparison.Ordinal) != 0)
return false;
var name = bindingName.Remove(0, 2); // remove first 2 chars "Is"
if (!_enumNames.Contains(name))
return false;
elementName = name;
return true;
}
#endregion Private Methods
#endregion Methods
//------------------------------------------------------------------------------------------------------------------------------------------
#region Events
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion Events
}
}
To respond to changes you can either subscribe to the NotifyPropertyChanged event or pass an anonymous method to the constructor as done above.
And finally the MainWindow.xaml
<Window x:Class="EnumSelectorTest.MainWindow"
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"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<RadioButton IsChecked="{Binding Selector.IsRed}">Red</RadioButton>
<RadioButton IsChecked="{Binding Selector.IsMagenta}">Magenta</RadioButton>
<RadioButton IsChecked="{Binding Selector.IsBlue}">Blue</RadioButton>
<RadioButton IsChecked="{Binding Selector.IsCyan}">Cyan</RadioButton>
<RadioButton IsChecked="{Binding Selector.IsGreen}">Green</RadioButton>
<RadioButton IsChecked="{Binding Selector.IsYellow}">Yellow</RadioButton>
<TextBlock Text="{Binding ColorName}"/>
</StackPanel>
</Grid>
</Window>
Hope someone else finds this useful, 'cause I reckon this ones going in my toolbox.
You have to add the Group Name for the Radio button
<StackPanel>
<RadioButton Content="Button A" IsChecked="{Binding Path=ButtonAIsChecked, Mode=TwoWay}" GroupName="groupName" />
<RadioButton Content="Button B" IsChecked="{Binding Path=ButtonBIsChecked, Mode=TwoWay}" GroupName="groupName" />
</StackPanel>
I have a very similar problem in VS2015 and .NET 4.5.1
XAML:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="6" Rows="1"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate >
<RadioButton GroupName="callGroup" Style="{StaticResource itemListViewToggle}" Click="calls_ItemClick" Margin="1" IsChecked="{Binding Path=Selected,Mode=TwoWay}" Unchecked="callGroup_Checked" Checked="callGroup_Checked">
....
As you can see in this code i have a listview, and items in template are radiobuttons that belongs to a groupname.
If I add a new item to the collection with the property Selected set to True it appears checked and the rest of buttons remain checked.
I solve it by getting the checkedbutton first and set it to false manually but this is not the way it's supposed to be done.
code behind:
`....
lstInCallList.ItemsSource = ContactCallList
AddHandler ContactCallList.CollectionChanged, AddressOf collectionInCall_change
.....
Public Sub collectionInCall_change(sender As Object, e As NotifyCollectionChangedEventArgs)
'Whenever collection change we must test if there is no selection and autoselect first.
If e.Action = NotifyCollectionChangedAction.Add Then
'The solution is this, but this shouldn't be necessary
'Dim seleccionado As RadioButton = getCheckedRB(lstInCallList)
'If seleccionado IsNot Nothing Then
' seleccionado.IsChecked = False
'End If
DirectCast(e.NewItems(0), PhoneCall).Selected = True
.....
End sub
`
<RadioButton IsChecked="{Binding customer.isMaleFemale}">Male</RadioButton>
<RadioButton IsChecked="{Binding customer.isMaleFemale,Converter= {StaticResource GenderConvertor}}">Female</RadioButton>
Below is the code for IValueConverter
public class GenderConvertor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
}
this worked for me. Even value got binded on both view and viewmodel according to the radio button click. True--> Male and False-->Female
Based on my code below, I want to be able to change the background color of a Button 2 when Button 1 is clicked.
XAML File
<Grid>
<Button x:Name="Button1"
Content="Button 1"
Command="{Binding Button1Command}"/>
<Button x:Name="Button2"
Content="Button 2"/>
</Grid>
ViewModel File
public class MyViewModel : ViewModelBase
{
public ICommand Button1Command{get;private set;}
public MyViewModel(){
Button1Command = new RelayCommand(() => button1_Click());
}
private void button1_Click()
{
Console.WriteLine("Button 1 clicked");
// how can I change the background color of Button 2 here
this.Dispatcher.Invoke(() => {
Button2.Background = Brushes.Red;
});
}
}
In addition to what pm_2 mentioned, you could take advantage of MVVMLight's Messenger class. The VM can send a message that is received by the View to change the background.
public class ChangeBackgroundMessage
{
public Brush TheColor { get; set; }
}
And then in your VM:
Button1Command = new RelayCommand(() => ExecuteButtonCommand());
....
private void ExecuteButtonCommand()
{
Messenger.Default.Send<ChangeBackgroundMessage>(new ChangeBackgroundMessage { TheColor = Brushes.Red } );
}
and in your View:
public partial class MyView : UserControl
{
public MyView()
{
InitializeComponent();
Messenger.Default.Register<ChangeBackgroundMessage>(this, m => ReceiveChangeBackgroundMessage(m);
}
private void ReceiveChangeBackgroundMessage(ChangeBackgroundMessage m)
{
// If you need to ensure this executes only on UI thread, use the
// DispatcherHelper class
DispatcherHelper.CheckBeginInvokeOnUI(() => button2.Background = m.TheColor);
}
}
Yet another alternative would be to have a "view service" that the View registers with it's ViewModel. For example:
public interface IMySpecificViewService
{
void ChangeButtonColor(Brush color);
}
In VM:
public IMySpecificViewService ViewService { get; set; }
and in View
public partial class MyView : UserControl, IMySpecificViewService
...
public MyView()
{
var vm = (MyViewModel)this.DataContext;
vm.ViewService = (IMySpecificViewService)this;
}
public void ChangeButtonColor(Brush color)
{
Button2.Background = color;
}
which can be called in your VM's command handler:
private void ExecuteButtonCommand()
{
ViewService?.ChangeButtonColor(Brushes.Red);
}
I find I use these approaches when I can't directly bind to a property in the VM, (or I don't want to bleed any View specific stuff in the VM) and I need more fine grained control over manipulating the controls.
There are two approaches to this that spring to mind - the first is to simply bind the background colour of Button2 to a property on the viewmodel. You could expose this from the view model as a brush; although the way that is more consistent with MVVM would be to create a value converter.
The idea being that the background of Button2, despite being linked to Button1, is actually linked to a state that has changed when Button1 is pressed; the value converter then maps the state (which is the domain of the ViewModel) with the colour (the domain of the view).
Doing is this way, means that you can change the state in the view model command of button1, but not have to involve the button1_click event, as it is now unnecessary.
This question illustrates how you might achieve this.
First of all you need to declare a property in your view model that will control the background color as well as a command handler which a button can call to toggle it. This might seem a little verbose but you soon get used to that with MVVM, and there are frameworks you can use to minimize that if it really bothers you. So here's the main view model:
public class MainViewModel : ViewModelBase
{
#region Background Color Flag
private bool _Flag;
public bool Flag
{
get { return this._Flag; }
set
{
if (this._Flag != value)
{
this._Flag = value;
RaisePropertyChanged(() => this.Flag);
}
}
}
#endregion Background Color Flag
#region Button Command Handler
private ICommand _ButtonCommand;
public ICommand ButtonCommand
{
get { return this._ButtonCommand = (this._ButtonCommand ?? new RelayCommand(OnButtonPressed)); }
}
private void OnButtonPressed()
{
this.Flag = !this.Flag;
}
#endregion Button Command Handler
public MainViewModel()
{
}
}
One of the objectives of MVVM is to have as loose coupling between the view and the view model as possible. The Button's command binding should be fairly straightforward, but to set the background of the second button you can use DataTriggers:
<StackPanel Orientation="Vertical">
<Button Content="Toggle Background" HorizontalAlignment="Left" VerticalAlignment="Top"
Command="{Binding ButtonCommand}" />
<Button Content="Hello World!" HorizontalAlignment="Left" VerticalAlignment="Top">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Flag}" Value="False">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Flag}" Value="True">
<Setter Property="Background" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
This will cause the second button's background to toggle between red and green as you click the first button:
I have an .NET 4.0 application using Caliburn.Micro. I want to create a dynamic menu such that I don't need to write XAML code for each menu item. Additionally, I want to associate each command with a key gesture.
I have an interface IAction:
public interface IAction
{
string Name { get; }
InputGesture Gesture { get; }
ICommand Command { get; }
}
In my ViewModel I expose a list of IActions:
private List<IAction> _actions;
public List<IAction> Actions
{
get { return _actions; }
set
{
_actions = value;
NotifyOfPropertyChange(()=> Actions);
}
}
I bind my Toolbar to the actions as follows:
<ToolBar>
<Menu ItemsSource="{Binding Actions}">
<Menu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</Menu.ItemContainerStyle>
</Menu>
</ToolBar>
All of the above works.
What I'm missing is the databinding of the Key Gesture.
Everywhere I read, I only find examples with static definitions of Window.InputBindings such as:
<Window.InputBindings>
<KeyBinding Key="B" Modifiers="Control" Command="ApplicationCommands.Open" />
</Window.InputBindings>
It would be great if I simply could encapsulate the Window.InputBindings in an ItemsControl, but that doesn't work.
Any of you know how to dynamically bind Window.InputBindings?
Thanks!
Key gestures have to be created for the window object (if they are to have window-wide effect).
I guess you could create a custom derived window object which would have a dependency property named for example BindableInputBindings. This property in its OnChanged callback would add/remove the key bindings every time the source collection changed.
EDIT: There may be some errors.
public class WindowWithBindableKeys: Window {
protected static readonly DependencyProperty BindableKeyBindingsProperty = DependencyProperty.Register(
"BindableKeyBindings", typeof(CollectionOfYourKeyDefinitions), typeof(WindowWithBindableKeys), new FrameworkPropertyMetadata("", new PropertyChangedCallback(OnBindableKeyBindingsChanged))
);
public CollectionOfYourKeyDefinitions BindableKeyBindings
{
get
{
return (string)GetValue(BindableKeyBindingsProperty);
}
set
{
SetValue(BindableKeyBindingsProperty, value);
}
}
private static void OnBindableKeyBindingsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as WindowWithBindableKeys).InputBindings.Clear();
// add the input bidnings according to the BindableKeyBindings
}
}
Then in XAML
<mynamespace:WindowWithBindableKeys BindableKeyBindings={Binding YourSourceOfKeyBindings} ... > ...
I have the following class:
public class Day
{
public int Date { get; set; }
public String DayName { get; set; }
public Day()
{
}
public Day(int date, string dayName)
{
Date = date;
DayName = dayName;
CommandManager.RegisterClassCommandBinding(typeof(Day), new CommandBinding(DayClick, new ExecutedRoutedEventHandler(OnExecutedDayClick),
new CanExecuteRoutedEventHandler(OnCanExecuteDayClick)));
}
public static readonly RoutedCommand DayClick = new RoutedCommand("DayClick", typeof(Day));
private static void OnCanExecuteDayClick(object sender, CanExecuteRoutedEventArgs e)
{
((Day)sender).OnCanExecuteDayClick(e);
}
private static void OnExecutedDayClick(object sender, ExecutedRoutedEventArgs e)
{
((Day)sender).OnExecutedDayClick(e);
}
protected virtual void OnCanExecuteDayClick(CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
e.Handled = false;
}
protected virtual void OnExecutedDayClick(ExecutedRoutedEventArgs e)
{
string content = String.Format("Day {0}, which is {1}, was clicked.", Date.ToString(), DayName);
MessageBox.Show(content);
e.Handled = true;
}
}
I am using the following DataTemplate (that is in a ResourceDictionary) to render it:
<DataTemplate DataType="{x:Type local:Day}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Grid.ColumnSpan="2" x:Name="rectHasEntry" Fill="WhiteSmoke"/>
<TextBlock Grid.Column="0" x:Name="textBlockDayName" Text="{Binding DayName}"
FontFamily="Junction" FontSize="11" Background="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,2,0,0"/>
<TextBlock Grid.Column="1" x:Name="textBlockDate" Text="{Binding Date}"
FontFamily="Junction" FontSize="11" Background="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,2,0,0"/>
<Rectangle Grid.ColumnSpan="2" x:Name="rectMouseOver" Fill="#A2C0DA" Opacity="0"
Style="{StaticResource DayRectangleMouseOverStyle}">
</Rectangle>
</Grid>
</DataTemplate>
No problems so far, I can get it on screen.
What I want to be able to do is assign a Command, or use an event, so that when the user clicks on the Day it will notify the parent of the Day object that it has been clicked.
I've tried the following:
<Rectangle.CommandBindings>
<CommandBinding Command="{x:Static local:Day.NextDay}"
Executed="{x:Static local:Day.OnExecutedDayClick}"
CanExecute="{x:Static local:Day.OnCanExecuteDayClick}"/>
</Rectangle.CommandBindings>
to try and bind the commands that are in the Day class but it didn't work. I got an error stating:
'ResourceDictionary' root element requires a x:Class attribute to support event handlers in the XAML file. Either remove the event handler for the Executed event, or add a x:Class attribute to the root element.
Which I think means that there is no code-behind file for a ResourceDictionary, or something to that effect.
In any event, I'm not sure if I should be using Commands here, or somehow tying events to the Rectangle in question, or if this is even possible. I've seen various places where it sure looks like it's possible, I'm just unable to translate what I'm seeing into something that actually works, hence this post.
Thanks in advance.
You cann't declare CommandBinding here, in this case you can assign the command here in DataTemplate and declare CommandBinding in your main Window or Page.
Edit:
In this way you can use Commands with your custom control.
Create a custom control and Declare Commands and Command Bindings also inside the control itself as in this Sample.
MyCustomControl.cs
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
InitializeCommands();
}
private static RoutedCommand _myCommand;
public static RoutedCommand MyCommand
{
get
{
return _myCommand;
}
}
private static void InitializeCommands()
{
_myCommand = new RoutedCommand("MyCommand", typeof(MyCustomControl));
CommandManager.RegisterClassCommandBinding(typeof(MyCustomControl),
new CommandBinding(_myCommand , OnMyCommandExecute));
}
private static void OnMyCommandExecute(object sender, ExecutedRoutedEventArgs e)
{
MyCustomControl control = sender as MyCustomControl;
if (control != null)
{
//logic for this command
}
}
and in your generic.xaml write this style and assign commands like this:
generic.xaml
<Style TargetType="{x:Type local:MyCustomControl}">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid>
<RepeatButton Command="{x:Static local:MyCustomControl.MyCommand}" >Click</RepeatButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>