So it's been a while since I posted last, but recently I had the opportunity to do some serious XAML binding stuff, which I thought I'd share with you.
If you've ever wanted an easy way to create an editable gridview inside a listview, it can seem like a pretty daunting task, but actually, it's really not that tough. You can setup a binding scenario that will do 80% of the work for you. Try the following out for size:
First, the XAML code:
<Window x:Class="EditableGridViewExample.Window1"
xmlns:local="clr-namespace:EditableGridViewExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="b2v" />
<Style TargetType="{x:Type TextBlock}" x:Key="TextBlockStyle">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource b2v}" ConverterParameter="False" >
<Binding ElementName="EditModeCheckBox" Path="IsChecked" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}" Path="IsSelected" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="{x:Type TextBox}" x:Key="TextBoxStyle">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource b2v}" ConverterParameter="True" >
<Binding ElementName="EditModeCheckBox" Path="IsChecked" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}" Path="IsSelected" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CheckBox x:Name="EditModeCheckBox" Content="Edit Mode" />
<ListView Grid.Row="1" x:Name="WindowListView" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumnHeader Tag="ColumnA" Content="Object" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=ColumnA}" Style="{StaticResource TextBlockStyle}" />
<TextBox Text="{Binding Path=ColumnA}" Style="{StaticResource TextBoxStyle}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumnHeader Tag="ColumnB" Content="Type" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=ColumnB}" Style="{StaticResource TextBlockStyle}"/>
<TextBox Text="{Binding Path=ColumnB}" Style="{StaticResource TextBoxStyle}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Now, the codebehind (I put everything in one file to make it easy to use):
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Data;
namespace EditableGridViewExample
{
public class ListItems : ObservableCollection<ListData>
{
/// <summary>
/// Initializes a new instance of the <see cref="ListItems"/> class.
/// </summary>
public ListItems()
{
this.Add(new ListData() { ColumnA = "apple", ColumnB = "fruit" });
this.Add(new ListData() { ColumnA = "jaguar", ColumnB = "animal" });
this.Add(new ListData() { ColumnA = "lullaby", ColumnB = "music" });
this.Add(new ListData() { ColumnA = "monkey", ColumnB = "animal" });
this.Add(new ListData() { ColumnA = "orange", ColumnB = "fruit" });
this.Add(new ListData() { ColumnA = "whale", ColumnB = "mammal" });
this.Add(new ListData() { ColumnA = "coathanger", ColumnB = "other" });
}
}
public class BoolToVisibilityConverter : IMultiValueConverter
{
/// <summary>
/// Converts source values to a value for the binding target. The data binding engine calls this method when it propagates the values from source bindings to the binding target.
/// </summary>
/// <param name="values">The array of values that the source bindings in the <see cref="T:System.Windows.Data.MultiBinding"/> produces. The value <see cref="F:System.Windows.DependencyProperty.UnsetValue"/> indicates that the source binding has no value to provide for conversion.</param>
/// <param name="targetType">The type of the binding target property.</param>
/// <param name="parameter">The converter parameter to use.</param>
/// <param name="culture">The culture to use in the converter.</param>
/// <returns>
/// A converted value.If the method returns null, the valid null value is used.A return value of <see cref="T:System.Windows.DependencyProperty"/>.<see cref="F:System.Windows.DependencyProperty.UnsetValue"/> indicates that the converter did not produce a value, and that the binding will use the <see cref="P:System.Windows.Data.BindingBase.FallbackValue"/> if it is available, or else will use the default value.A return value of <see cref="T:System.Windows.Data.Binding"/>.<see cref="F:System.Windows.Data.Binding.DoNothing"/> indicates that the binding does not transfer the value or use the <see cref="P:System.Windows.Data.BindingBase.FallbackValue"/> or the default value.
/// </returns>
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool param = bool.Parse(parameter as string);
bool combined = true;
foreach (bool val in values) combined &= val;
return combined == param ? Visibility.Visible : Visibility.Hidden;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ListData : DependencyObject
{
public static readonly DependencyProperty ColumnAProperty =
DependencyProperty.Register("ColumnAProperty", typeof(string),
typeof(ListData), new UIPropertyMetadata(null));
public static readonly DependencyProperty ColumnBProperty =
DependencyProperty.Register("ColumnBProperty", typeof(string),
typeof(ListData), new UIPropertyMetadata(null));
/// <summary>
/// Gets or sets the column A.
/// </summary>
/// <value>
/// The column A.
/// </value>
public string ColumnA
{
get { return (string)GetValue(ColumnAProperty); }
set { SetValue(ColumnAProperty, value); }
}
/// <summary>
/// Gets or sets the column B.
/// </summary>
/// <value>
/// The column B.
/// </value>
public string ColumnB
{
get { return (string)GetValue(ColumnBProperty); }
set { SetValue(ColumnBProperty, value); }
}
}
public partial class Window1 : Window
{
/// <summary>
/// Initializes a new instance of the <see cref="MainWindow"/> class.
/// </summary>
public Window1()
{
InitializeComponent();
// bind datacontext of the list view to a new listitems observable collection
this.WindowListView.DataContext = new ListItems();
}
}
}
Oh, and if you're wondering how I got it to format so nicely, I'm using this the following
blogspot source code formatting tool.
No comments:
Post a Comment