I want to present several ToggleButton/RadioButton elements that:
- Map to an enumeration, meaning the DataContext has a "public Mode CurrentMode" property.
- Are mutually exclusive (only one button is checked)
- When a button is clicked, the state doesn't change immediately. Instead, a request is sent to a server. The state changes when the response arrives.
- Have a different image for checked/unchecked st开发者_运维问答ate
For example, 4 buttons would display the following view-model:
public class ViewModel
{
public enum Mode { Idle, Active, Disabled, Running }
Mode m_currentMode = Mode.Idle;
public Mode CurrentMode
{
get { return m_currentMode; }
set
{
SendRequest(value);
}
}
// Called externally after SendRequest, not from UI
public void ModeChanged(Mode mode)
{
m_currentMode = mode;
NotifyPropertyChanged("CurrentMode");
}
}
My initial approach was to use the solution from How to bind RadioButtons to an enum?, but that is not enough since the button state change immediately, even if I don't call NotifyPropertyChanged in the setter. In addition, I don't like the "GroupName" hack.
Any ideas? I don't mind creating a custom button class, as I need many buttons like that for multiple views.
I'm using .NET 3.5 SP1 and VS2008.
If you want to use the RadioButtons you just need to make some minor tweaks to workaround the default behavior of the RadioButton.
The first issue you need to workaround is the automatic grouping of RadioButtons based on their common immediate parent container. Since you don't like the "GroupName" hack your other option is to put each RadioButton inside of its own Grid or other container. This will make each button a member of its own group and will force them to behave based on their IsChecked binding.
<StackPanel Orientation="Horizontal">
<Grid>
<RadioButton IsChecked="{Binding Path=CurrentMode, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Idle}">Idle</RadioButton>
</Grid>
<Grid>
<RadioButton IsChecked="{Binding Path=CurrentMode, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Active}">Active</RadioButton>
</Grid>
<Grid>
<RadioButton IsChecked="{Binding Path=CurrentMode, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Disabled}">Disabled</RadioButton>
</Grid>
<Grid>
<RadioButton IsChecked="{Binding Path=CurrentMode, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Running}">Running</RadioButton>
</Grid>
</StackPanel>
This brings me to the next workaround which is ensuring the button clicked on doesn't stay in its Checked state after clicking on it which was needed in order to trigger the set call because you are binding on the IsChecked property. You will need to send out an additional NotifyPropertyChanged, but it must be pushed into the queue of the Dispatch thread so the button will receive the notification and update its visual IsChecked binding. Add this to your ViewModel class, which is probably replacing your existing NotifyPropertyChanged implementation and I am assuming your class is implementing the INotifyPropertyChanged which is missing in the question's code:
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
Dispatcher uiDispatcher = Application.Current != null ? Application.Current.Dispatcher : null;
if (uiDispatcher != null)
{
uiDispatcher.BeginInvoke(DispatcherPriority.DataBind,
(ThreadStart)delegate()
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}
}
}
Then in your CurrentMode's Setter call NotifyPropertyChanged("CurrentMode"). You probably already needed something like this since your Server's ModeChanged call is probably coming in on a thread that isn't the Dispatcher thread.
Finally you will need to apply a Style to your RadioButtons if you want them to have a different Checked/Unchecked look. A quick Google search for WPF RadioButton ControlTemplate eventually came up with this site: http://madprops.org/blog/wpf-killed-the-radiobutton-star/.
精彩评论