WPF Radio Button Selected Value Command Binding - wpf

In WPF I have a stack panel whose items control defines a data template. This data template is a radio button. The items source is a collection of names on my view model.
So for each name in the view model, a radio button appears on the stack panel with the text beside it for that name (using the content property).
All of these radio buttons have the group set to "name" so selection is mutually exclusive.
My question is, what are my options for binding the content of the selected radio button to a property on my view model "selectedName"?
Ideally I want a UI binding, that is code-free.
Thankyou

I am not sure if you can make use of the mutually exclusiveness there without any event handling. Normally i have this problem with MenuItems or button groups and my approach is to use Multibindings with a EqualityConverter, e.g.
<Setter Property="IsChecked">
<Setter.Value>
<MultiBinding Converter="{StaticResource EqualityComparisonConverter}" Mode="OneWay">
<!-- This binding should find your VM and bind to your property -->
<Binding RelativeSource="{RelativeSource AncestorType=Window}"
Path="DataContext.SelectedName"/>
<!-- Binds to the item being templated -->
<Binding />
</MultiBinding>
</Setter.Value>
</Setter>
A converter (it's not very safe, throws exception if one of the values is null, might want to improve it):
public class EqualityComparisonConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2) throw new Exception("At least two inputs are needed for comparison");
bool output = values.Aggregate(true, (acc, x) => acc && x.Equals(values[0]));
return output;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
Actually it's somewhat of a mystery to me how that even works (considering that the binding is one-way)...

You could create a ViewModel for each RadioButton. It should expose property to bind RadioButton's Checked and some events to notify master ViewModel about it.

Related

Conditional styling of an element in visual structure

I have trouble making a universal solution for next problem:
Imagine a custom TreeView control that a;;pws theming - there is a list of elements in separate xaml file that is used to build a visual structure for TreeView control. There is onm Border element that I would like to paint its background based on type of data that is dsiplayed. This border element is part of "+" sign, and is not exposed through a style.
I can do this using code behind, and subscribing to some of the events that this control provides, then find this border in child elements, and change its background. However, this code will be repeated in many views, sometimes exactly the same, sometimes with slightly different modifications (ex only a different data element is checked for type).
Is there a way I can do this using any other technique? It seems that style selectors can be used here, since visual structure is not built at that poinr, so I cannot search this element by its name. And this element is not exposed through any property on the control.
Edit:
currently I am having my oqwn control that inherit this control, if you find thazt it can be done using Attached/depenency propertyies:
public class MyTreeView : CustomTreeView
{
}
If I ere to poaint a background for a row I would create a style selector:
<local:ProductRowtyleSelector x:Key="productRowStyleSelector"
DefaultStyle="{StaticResource defaultProductRowStyle}"
GoodStyle="{StaticResource goodProductRowStyle}"
ScrapStyle="{StaticResource reworkProductRowStyle}" />
Where GoodStyle would inherit the style for a row that exists in control template. This way I would use style selecor anywhere I displayt list of Products.
Is there a similar way that I would do the same for the Border element that I added in control template?
Edit
Ok if I get what you need, you can try this
Create a IsTypeOf IValueConverter like this :
public class IsTypeOfConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Type parameterType = parameter as Type;
if (parameterType == null)
throw new ArgumentException();
return parameterType.IsAssignableFrom(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
Then in your xaml, put a DataTrigger in the applicable style
<DataTrigger Binding="{Binding Converter={StaticResource IsKindOfConverter}, ConverterParameter={x:Type Person}}">
<Setter Property="Backgroud" Value="Red" />
</DataTrigger>

MVVM - hiding a control when bound property is not present

I was wondering if it is possible to hide a control on a view if the property to which the control is bound does not exist in the view model. For example, if I have the following:
<CheckBox Content="Quote"
IsChecked="{Binding Path=IsQuoted}" />
Can I detect in XAML that the IsQuoted property does not exist on the view model, and simply hide the control in that instance.
I am essentially creating a wizard dialog that moves through a collection of view models, displaying the associated view for each one. For some of the view models in the collection, the "IsQuoted" property will be present, and for some not.
I would like to have a check box outside of these views that displays when the current view model has the property, and hides when the view model does not. All of the view models are derived from a common base class, but I would rather not clutter the base by adding a "ShowQuoted" property, etc.
Thoughts? And, thanks in advance...
Handle the case where it the value is present by using a converter which always returns Visibility.Visible. Handle the case where the value isn't present by specifying a fallback value. When the property isn't present the binding fails and receives the fallback value.
<Page.DataContext>
<Samples:OptionalPropertyViewModel/>
</Page.DataContext>
<Grid>
<Grid.Resources>
<Samples:AlwaysVisibleConverter x:Key="AlwaysVisibleConverter" />
</Grid.Resources>
<CheckBox
Content="Is quoted"
IsChecked="{Binding IsQuoted}"
Visibility="{Binding IsQuoted,
Converter={StaticResource AlwaysVisibleConverter},
FallbackValue=Collapsed}"
/>
</Grid>
public class OptionalPropertyViewModel
{
public bool IsQuoted { get; set; }
}
public class AlwaysVisibleConverter : IValueConverter
{
#region Implementation of IValueConverter
public object Convert(object value,
Type targetType, object parameter, CultureInfo culture)
{
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}

Selecting Current MenuItem in wpf

How can I select the currentItem in a menuitem collection. Like one would do with a listbox. I tried wrapping the collection in a collectionViewSource, However that brought be no such luck.
Thanks in advance.
The MenuBase, which ContextMenu and Menu derive from, inherits ItemsControl, which does not include the concept of SelectedItem. That's something that ListBox adds.
You do, however, have the ItemsControl.ItemTemplate. Which is awesome.
One option would be to make your ItemTemplate a ToggleButton. This gives you a couple of things. Inherently, ToggleButtons can look like they're selected using their IsChecked property. Second, they have a Command property which you can bind to a command in your ViewModel.
So, if you have something along the lines of:
<Menu ItemsSource="{Binding ThingsToBindTo}">
<Menu.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Resources>
<conv:BindingProxy x:Key="proxy" Data="{Binding}" />
</Grid.Resources>
<ToggleButton Content="{Binding NameOrLabel}" CommandParameter="{Binding}" Command="{Binding Path=DataContext.SelectThingCommand, RelativeSource={RelativeSource AncestorType=Menu}}" >
<ToggleButton.IsChecked>
<Binding Mode="OneWay" Path="DataContext.SelectedThing" RelativeSource={RelativeSource AncestorType=Menu}">
<Binding.Converter>
<conv:ComparisonConverter CompareTo="{Binding Source={StaticResource proxy}, Path=Data}" />
</Binding.Converter>
</Binding>
</ToggleButton.IsChecked>
</ToggleButton>
</Grid>
</DataTemplate>
</Menu.ItemTemplate>
</Menu>
So this is a little complicated.
As per normal, you're binding to a list of items. ThingsToBindTo should be whatever your list is. Then you start defining your template. NameOrLabel is whatever property you want to appear on your toggle button. The command parameter is binding to the data item that the template is wrapping around by using nothing more than "{Binding}". The command is actually on the DataContext of your Menu, which is why RelativeSource is used here.
What this is saying is you're going to pass a command the thing that was just clicked. Effectively, you're selecting the button you click. Then, your command just needs to set a SelectedThing property in your ViewModel equal to whatever Thing is passed to it. Hopefully you have implemented a class that implements ICommand to create your delegate commands. If you don't, there are a lot of articles out there on how to do it. If you don't know how, put a comment on this post and I'll add the source code to do it.
Then we have the "IsChecked" bad boy. We're actually doing a binding long-hand there. This is the more complicated piece, but it allows a DataTemplated item to actually bind to itself within a converter.
First, you need the proxy object, which is explained here:
http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
Very simple to implement. Once it is done, the BindingProxy resource within your grid will work, and can act as an anchor back to the item bound to by the DataTemplate. The linked article explains why.
Then, you need a converter that compares two objects to each other.
public class ComparisonConverter : DependencyObject, IValueConverter
{
public object CompareTo
{
get { return (object)GetValue(CompareToProperty); }
set { SetValue(CompareToProperty, value); }
}
public static readonly DependencyProperty CompareToProperty =
DependencyProperty.Register("CompareTo", typeof(object), typeof(ComparisonConverter), new UIPropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (CompareTo != null)
{
return CompareTo.Equals(value);
}
else
{
return false;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
So now that binding will take the selected item from the DataContext of the menu, and compare it to whatever the ToggleButton is bound to. If the two objects match, the button appears clicked/selected. If they don't match, the button doesn't look selected.
So I do happen to have that BindingProxy and my converter in the same namespace. You don't necessarily have to do that. I just usually have a namespace for "Xaml Trick" classes that I have to program.
This is a lot to digest, and I'm happy to clarify anything.
One other thing...if you don't like the "ToggleButton" look, you can always style them to look completely different. The thing that having a ToggleButton buys you is the "IsChecked" property and the Command property. You can make the ContentTemplate look like anything you want, which gives you a lot of freedom in styling your menu.
If the ListBox has its ItemsSource set to a generic list of a complex entity, using ListBox.SelectedValue will get you the currently selected data.
For example:
public partial class NameListView : Window
{
/// <summary>
/// Constructor
/// </summary>
public NameListView()
{
List<string> names = new List<string>();
names.Add("John Doe");
names.Add("Jane Doe");
lbNameList.ItemsSource = names;
}
/// <summary>
/// Selection changed event handler for ListBox lbNameList
/// </summary>
void lbNameList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
string currentValue = lbNameList.SelectedValue.ToString();
MessageBox.Show("Currently selected value: " + currentValue);
}
}
}
If you have a property in your contextfile ( like Codebehind file or ViewModel ) that represents the currentSelectedItem then you can write the following in your xaml :
<ListView x:Name="MyList"
ItemsSource="MySource"
SelectedItem="{Binding Path=MyCurrentSelectedItem}" IsSynchronizedWithCurrentItem="True">
Codebehind / ViewModel
public MyType MyCurrentSelectedItem { get; set; }

Preventing the User to select a tab WPF Tab Item

I had to prevent the user from selecting a tabitem in a WPF TabControl,
1)unless and untill the user checks a check box in one condition the user should be shown a message box and if he checks the check box he can navigate to any other tab
2)Checking a particular condition the user shouldnt be able to get into a particular tab on selecting it,and I dont have an option of making the tab item collapse.and it should pop up a message box and get back to the same prv tab item selected
I have seen Smith Josh's sample code as below and this is what i exactly wanted for the 1st scenerio
http://joshsmithonwpf.wordpress.com/2009/09/04/how-to-prevent-a-tabitem-from-being-selected/
But I need something that works in MVVM, where my application has a strict "No CodeBehind"
You could inherit the TabControl (or add an attached property) which controls if navigation to another tab item is allowed; however, let me just stress that 'no codebehind' is kinda silly - there are plenty of times when code-behind can be used for view-only purposes, and that's ok.
Back to the problem... what you'd do using my suggestion is hide the code-behind (checking if the action is allowed) inside a control, so that the actual view (the page/window etc) doesn't contain it. If you declare the new property as a DependencyProperty you get all the binding facilities etc.
EDIT: I tested my other code and it didn't work. Was just an idea anyways. Here's a method that does work (although I agree with Alex that code behind in MVVM is fine when adjusting the View).
In this case I created a converter which takes two boolean values: If the tab is selected and if we can change tabs. If both of these are set to false, we return false to disable the tab. If either is set to true, we leave the tab enabled.
Here's the code. I have a property in my VM called CanChangeTabs and an instance of MyConverter in Window.Resources called Converter.
XAML inTabItem:
<TabItem.IsEnabled>
<MultiBinding Converter="{StaticResource Converter}">
<Binding RelativeSource="{RelativeSource Self}" Path="IsSelected" />
<Binding Path="CanChangeTabs" />
</MultiBinding>
</TabItem.IsEnabled>
Converter:
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (object value in values)
{
if ((bool)value)
{
return true;
}
}
return false;
}
public object[] ConvertBack(object values, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF - Dynamically access a specific item of a collection in XAML

I have a data source ('SampleAppearanceDefinitions'), which holds a single collection ('Definitions'). Each item in the collection has several properties, including Color, which is what I'm interested in here.
I want, in XAML, to display the Color of a particular item in the collection as text. I can do this just fine using this code below...
Text="{Binding Source={StaticResource SampleAppearanceDefinitions}, Path=Definitions[0].Color}"
The only problem is, this requires me to hard-code the index of the item in the Definitions collection (I've used 0 in the example above). What I want to do in fact is to get that value from a property in my current DataContext ('AppearanceID'). One might imagine the correct code to look like this....
Text="{Binding Source={StaticResource SampleAppearanceDefinitions}, Path=Definitions[{Binding AppearanceID}].Color}"
...but of course, this is wrong.
Can anyone tell me what the correct way to do this is? Is it possible in XAML only? It feels like it ought to be, but I can't work out or find how to do it.
Any help would be greatly appreciated!
Thanks!
AT
MultiBinding is your friend here:
Assuming you have a TextBlock:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource AppearanceIDConverter}">
<Binding Source="{StaticResource SampleAppearanceDefinitions}" />
<Binding Path="AppearanceID" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
And define a MultiValueConverter to return what you wish to see:
public class AppearanceIDConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<item> items = (List<item>)values[0]; //Assuming its items in a List
int id = (int)values[1]; //Assuming AppearanceID is an integer
return items.First(i => i.ID == id).Color; //Select your item based on the appearanceID.. I used LINQ, but a foreach will work just fine as well
}
public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
Of course, you will need to set the converter as a resource in your Resource dictionary, like you did SampleAppearanceDefinitions. You can also ditch the multibinding and use a regular binding to AppearanceID with a IValueConverter, if you can get to the SampleAppearanceDefinitions collection through code ;).
Hope this helps
Even if it could be possible you'd better not do that this way, but instead use a dedicated property in your view model or in the code behind of your view if it has only a pure graphical meaning.
This property, say "CurrentAppearance", would expose a Color property you could bind from your Xaml :
Text="{Binding CurrentAppearance.Color}"
which is more understandable.
As a general advice : avoid to spoil your Xaml with plumbing code : Xaml should be as readable as possible,
particularly if you work with a team of designers that have no coding skills and do not want to be concerned with the way you are managing the data.
Moreover, if later you decide to change the way data are managed you would not have to change your Xaml.
MultiBinding might actually work if your list is on a viewmodel instead of a staticresource. I was suprised myself to see that the object passed on to the view is actually a pointer to the object on the model, so changing the object in the view (eg. typing in new test in the textbox) directly affects the model object.
This worked for me. The ConvertBack method is never useed.
public class PropertyIdToPropertyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2)
{
var properties = values[0] as ObservableCollection<PropertyModel>;
if (properties != null)
{
var id = (int)values[1];
return properties.Where(model => model.Id == id).FirstOrDefault();
}
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Resources