Changing visibility of child controls in RowDetailsTemplate based on DataContext property - silverlight

I have a Silverlight DataGrid that contains a RowDetailsTemplate. The RowDetailsTemplate contains a TabControl with several TabItems. The DataGrid will be bound with items of type Contact. Contact has a property called ContactType. I would like to have several of the TabItems be hidden when ContactType is Client. Ideally I would like to do this through DataBinding but I haven't found anyway to do this yet.

Bind the TabItem.Visibility in the RowDetailTemplate to the ContactType using a Value Converter that converts ContactType to Visiblity. You should add the ContactTypeConverter to the app or page as a resource.
<TabItem
Visibility="{Binding ContactType, Converter={StaticResource ContactTypeConverter}}"/>
namespace Demo
{
using System;
using System.Windows;
using System.Windows.Data;
public enum ContactType
{
Client
};
/// <summary>
/// A Value converter
/// </summary>
public class ContactTypeConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var contactType = (ContactType) value;
switch (contactType)
{
case ContactType.Client:
return Visibility.Visible;
default:
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
#endregion
}
}

Related

Is there some way to "invert" a converter?

In WPF one has the possibility to use a converter in binding, so that one can bind for instance a Visibility property of a control to a Boolean property in the view model.
For this specific pairing (Visibility and Boolean) WPF does offer an out-of-the-box converter called BooleanToVisibilityConverter.
But let's say I'd like to bind a Boolean property of a control to a Visibility property in the view model. Is there any way to use the standard BooleanToVisibilityConverter and tell the binding to invert it (to use ConvertBack instead on Convert and vice versa)?
Or do I have to write another converter for that case?
So, there is no built-in way of inverting the converter. We can, however, work around that by introducing a "shim" converter like this one:
public class InverterConverter : IValueConverter
{
public IValueConverter Converter { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Converter.ConvertBack(value, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Converter.Convert(value, targetType, parameter, culture);
}
}
With the usage as follows:
<ContentControl>
<ContentControl.Content>
<Binding>
<Binding.Converter>
<InverterConverter Converter="{StaticResource YourConverter}" />
</Binding.Converter>
</Binding>
</ContentControl.Content>
</ContentControl>
This, obviously, is some heavy syntax but we can simplify it with this little markup extension:
public class InvertedExtension : MarkupExtension
{
public IValueConverter Converter { get; set; }
public InvertedExtension(IValueConverter converter)
{
Converter = new InverterConverter() { Converter = converter };
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Converter;
}
}
<ContentControl Content="{Binding Converter={Inverted {StaticResource MyConverter}}}" />
Is there any way to use the standard BooleanToVisibilityConverter and tell the binding to invert it (to use ConvertBack instead on Convert and vice versa)?
No.
Or do I have to write another converter for that case?
Yes.
You could implement a generic converter that accepts "true" and a "false" values of any type:
public class BooleanConverter<T> : IValueConverter
{
public T True { get; set; }
public T False { get; set; }
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
value is bool && ((bool)value) ? True : False;
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
value is T && EqualityComparer<T>.Default.Equals((T)value, True);
}
...and derive from this for each type that you want to handle:
public class BooleanToVisibilityNegationConverter : BooleanConverter<Visibility>
{
public BooleanToVisibilityNegationConverter()
: base()
{
True = Visibility.Hidden;
False = Visibility.Visible;
}
}

Set the Visibility of Data Grid in WPF

In my application I have 3 data grids in a single xaml file. Based on the User selection I want show one grid and hide other grids.
in my view model class I have Boolean property for each grid and based on the selection I am setting it to true or false.But all grids are visible .
<DataGrid Visibility="{Binding Path=IsGridVisible}" >
In my view model I am setting IsGridVisible value
public bool IsCapexGridVisible
{
get { return isCapexGridVisible; }
set { isCapexGridVisible = value; RaisePropertyChangedEvent("IsCapexGridVisible"); }
}
Please provide your ideas. Thanks
There is a BooleanToVisibilityConverter available to you that converts true to System.Windows.Visibility.Visible and false to System.Windows.Visibility.Collapsed.
So you can take help of this pre built converter and must add it to resources.
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
Create a property of type bool in your ViewModel
bool _dgVisibility;
public bool DataGridVisibility
{
get { return _dgVisibility; }
set
{
_dgVisibility = value;
OnPropertyChanged("DataGridVisibility");
}
}
and you can use it as below
<DataGrid Visibility="{Binding Path=DataGridVisibility, Converter={StaticResource BoolToVis}}"/>
Visibility property on UIElement is not a boolean. It is an enum with three values:
Collapsed Do not display the element, and do not reserve space for it in layout.
Hidden Do not display the element, but reserve space for the element in layout.
Visible Display the element.
So in order to set it properly from ViewModel you should:
- make your property type of Visibility (not best solution in the world)
- Use converter for the binding which will do the trick of translating boolean to visibility
public class BooleanToCollapsedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(Visibility) && value is bool)
{
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
throw new FormatException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Additional converter variant with visibility customization
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
[MarkupExtensionReturnType(typeof(IValueConverter))]
public class BoolToVisibilityConverter : MarkupExtension, IValueConverter
{
[ConstructorArgument("TrueValue")]
public Visibility TrueValue { get; set; }
[ConstructorArgument("FalseValue")]
public Visibility FalseValue { get; set; }
[ConstructorArgument("NullValue")]
public Visibility NullValue { get; set; }
public BoolToVisibilityConverter()
{
TrueValue = Visibility.Visible;
FalseValue = Visibility.Collapsed;
NullValue = Visibility.Collapsed;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return NullValue;
if (value is not bool boolValue)
return null;
return boolValue ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (Equals(value, TrueValue))
return true;
if (Equals(value, FalseValue))
return false;
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
Usage:
<someControl ...
xmlns:converters="clr-namespace:ExampleNamespace.Converters;assembly=ExampleAssembly"
...
>
...
Visibility="{Binding IsSearchInProgress,
Mode=OneWay,
Converter={converters:BoolToVisibilityConverter}}"
Visibility="{Binding IsSearchInProgress,
Mode=OneWay,
Converter={converters:BoolToVisibilityConverter TrueValue=Collapsed, FalseValue=Visible}}"

Custom IValueConverter inheriting from DependencyObject

I wanted to experiment with being able to have a converter whose arguments can be bound with the current data context. Can anyone tell me why when reaching the Convert() function, the Source property is always null?
namespace WpfApplication32
{
public class ConverterTest : DependencyObject, IValueConverter
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(DependencyObject), typeof(ConverterTest));
public DependencyObject Source
{
get { return (DependencyObject)this.GetValue(SourceProperty); }
set { this.SetValue(SourceProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
Value = 7;
InitializeComponent();
DataContext = this;
}
public float Value
{
get;
set;
}
}
}
<Window x:Class="WpfApplication32.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication32">
<Slider>
<Slider.Value>
<Binding Path="Value">
<Binding.Converter>
<local:ConverterTest Source="{Binding}"/>
</Binding.Converter>
</Binding>
</Slider.Value>
</Slider>
</Window>
One possible solution is to make your Converter inherit from Freezable instead (Hillberg Freezable trick). Then you can even define your Converter in your Resources and reference it in your binding as an attribute instead of an extra child element.

Bound Silverlight Combobox not showing default value with EnumBinder

My models use enums for fixed multiple selections. I'm using Dean Chalk's EnumBinder, found at http://deanchalk.me.uk/post/Enumeration-Binding-In-Silverlight.aspx , to bind to a combo box. Everything seems to work great except the default value isn't shown. The selected index is -1, and it doesn't matter if I bind to SelectedItem or SelectedValue. The combobox works fine otherwise. I have no problems with default values with any other bound comboboxes.
enumbindingsupport.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
/* All of this was once just part of the RichClient sub-namespace
* but it has its uses elsewhere, so it has been moved.
*/
/// <summary>
/// Container for enumeration values.
/// </summary>
/// <remarks>
/// http://deanchalk.me.uk/post/Enumeration-Binding-In-Silverlight.aspx
/// </remarks>
public sealed class EnumContainer
{
public int EnumValue { get; set; }
public string EnumDescription { get; set; }
public object EnumOriginalValue { get; set; }
public override string ToString() {
return EnumDescription;
}
public override bool Equals(object obj) {
if (obj == null)
return false;
if (obj is EnumContainer)
return EnumValue.Equals((int)((EnumContainer)obj).EnumValue);
return EnumValue.Equals((int)obj);
}
public override int GetHashCode() {
return EnumValue.GetHashCode();
}
}
/// <summary>
/// A collection to store a list of EnumContainers that hold enum values.
/// </summary>
/// <remarks>
/// http://deanchalk.me.uk/post/Enumeration-Binding-In-Silverlight.aspx
/// </remarks>
/// <typeparam name="T"></typeparam>
public class EnumCollection<T> : List<EnumContainer> where T : struct
{
public EnumCollection() {
var type = typeof(T);
if (!type.IsEnum)
throw new ArgumentException("This class only supports Enum types");
var fields = typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (var field in fields) {
var container = new EnumContainer();
container.EnumOriginalValue = field.GetValue(null);
container.EnumValue = (int)field.GetValue(null);
container.EnumDescription = field.Name;
var atts = field.GetCustomAttributes(false);
foreach (var att in atts)
if (att is DescriptionAttribute) {
container.EnumDescription = ((DescriptionAttribute)att).Description;
break;
}
Add(container);
}
}
}
enumvalueconverter.cs
using System;
using System.Globalization;
using System.Windows.Data;
/// <summary>
/// Supports two-way binding of enumerations.
/// </summary>
public class EnumValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture) {
return (int)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture) {
if (value == null)
return null;
if (value.GetType() == targetType)
return value;
return ((EnumContainer)value).EnumOriginalValue;
}
}
enum used:
/// <summary>
/// Describes the available sorting options.
/// </summary>
public enum PeopleSortOptionsEnum
{
[Display(Order = 10)]
[Description("First Name, Last Name")]
FirstNameThenLastName,
[Display(Order = 20)]
[Description("Last Name, First Name")]
LastNameThenFirstName,
[Display(Order = 30)]
Grade,
[Display(Order = 40)]
Gender,
[Display(Order = 50)]
Age
}
property on my model:
/// <summary>
/// This is the list for the enumeration to bind to.
/// </summary>
public EnumCollection<PeopleSortOptionsEnum> AvailableSortOptions
{
get { return new EnumCollection<PeopleSortOptionsEnum>(); }
}
XAML snippet:
<ComboBox ItemsSource="{Binding Path=AvailableSortOptions, Mode=OneWay}" SelectedValue="{Binding Path=Preferences.SortOrder, Mode=TwoWay, Converter={StaticResource EnumConverter}}" Grid.Column="1" Grid.Row="2" Height="32" Margin="48,31,0,0" x:Name="cboSort" VerticalAlignment="Top" />
Where Preferences.SortOrder is of type PeopleSortOptionsEnum, the converter is in my app.xaml as a converter for all enum types.
Anyone have any idea why it won't set the index to the currently selected value? I'm about to just throw some code in the codebehind to set the selectedindex to the currently selected value on load, but I feel so dirty just thinking about it.
Besides this issue, it works wonderfully, so thanks Dean!
Edit Adding the Preferences.SortOrder code:
public PeopleSortOptionsEnum SortOrder
{
get { return sortOrder; }
set
{
if (sortOrder != value)
{
sortOrder = value;
PropertyHasChanged("SortOrder");
}
}
}
The issue is the Convert method on your enum converter class:
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture) {
return (int)value;
}
When the combobox SelectedItem is gets the value from SortOrder it is return in Enum value which this converter is converting to an int. However, the combobox items is a collection of EnumContainer objects, not ints. So it fails to set the selected item.
To resolve the issue you have to do two things. First change your combobox bindings and set the SelectedValuePath:
<ComboBox ItemsSource="{Binding Path=AvailableSortOptions}"
SelectedValue="{Binding Path=SortOrder, Mode=TwoWay, Converter={StaticResource EnumConverter}}"
SelectedValuePath="EnumOriginalValue"/>
Second you have to slightly modify the Convert method on your converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
Once I made those two changes it started working as expected.

radio button binding in WPF MVVM

can any one tell me how can we enable/ disable button by radio button in MVVM.
Generally, it does not require view model. You can bind properties of two elements directly by using NotConverter.
[ValueConversion(typeof(bool), typeof(bool))]
public class NotConverter : IValueConverter
{
public static readonly IValueConverter Instance = new NotConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool typedValue = (bool)value;
return !typedValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Convert(value, targetType, parameter, culture);
}
}
< RadioButton Name=radio />
< Button IsEnabled={Binding Path=IsChecked, ElementName=radio, Converter={x:Static ns:NotConverter.Instance}} />
The ViewModel sample application of the WPF Application Framework (WAF) shows how to bind ViewModel properties to RadioButtons.

Resources