.net - template - wpf listview grouping
WPF: ¿Cómo ocultar GridViewColumn usando XAML? (9)
Tengo el siguiente objeto en App.xaml
<Application.Resources>
<ResourceDictionary>
<GridView x:Key="myGridView" x:Shared="false">
<GridViewColumn Header="Created" DisplayMemberBinding="{Binding Path=Created}"/>
... more code ...
Y utilizo esta vista de cuadrícula en varios lugares. Ejemplo:
<ListView x:Name="detailList" View="{StaticResource myGridView}" ...>
En uno de los usos (como en la lista de detalles arriba), me gustaría ocultar la columna Creada, posiblemente utilizando XAML.
¿Algunas ideas?
Basado en la respuesta de Ben McMillan, pero admite cambios dinámicos de propiedad visible. He simplificado aún más su solución al eliminar la propiedad IsEnabled.
public class GridViewColumnVisibilityManager
{
static Dictionary<GridViewColumn, double> originalColumnWidths = new Dictionary<GridViewColumn, double>();
public static bool GetIsVisible(DependencyObject obj)
{
return (bool)obj.GetValue(IsVisibleProperty);
}
public static void SetIsVisible(DependencyObject obj, bool value)
{
obj.SetValue(IsVisibleProperty, value);
}
public static readonly DependencyProperty IsVisibleProperty =
DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(GridViewColumnVisibilityManager), new UIPropertyMetadata(true, OnIsVisibleChanged));
private static void OnIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
GridViewColumn gc = d as GridViewColumn;
if (gc == null)
return;
if (GetIsVisible(gc) == false)
{
originalColumnWidths[gc] = gc.Width;
gc.Width = 0;
}
else
{
if (gc.Width == 0)
gc.Width = originalColumnWidths[gc];
}
}
}
En realidad, encuentro que la solución más fácil es a través de propiedades adjuntas:
public class GridViewColumnVisibilityManager
{
static void UpdateListView(ListView lv)
{
GridView gridview = lv.View as GridView;
if (gridview == null || gridview.Columns == null) return;
List<GridViewColumn> toRemove = new List<GridViewColumn>();
foreach (GridViewColumn gc in gridview.Columns)
{
if (GetIsVisible(gc) == false)
{
toRemove.Add(gc);
}
}
foreach (GridViewColumn gc in toRemove)
{
gridview.Columns.Remove(gc);
}
}
public static bool GetIsVisible(DependencyObject obj)
{
return (bool)obj.GetValue(IsVisibleProperty);
}
public static void SetIsVisible(DependencyObject obj, bool value)
{
obj.SetValue(IsVisibleProperty, value);
}
public static readonly DependencyProperty IsVisibleProperty =
DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(GridViewColumnVisibilityManager), new UIPropertyMetadata(true));
public static bool GetEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(EnabledProperty);
}
public static void SetEnabled(DependencyObject obj, bool value)
{
obj.SetValue(EnabledProperty, value);
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(GridViewColumnVisibilityManager), new UIPropertyMetadata(false,
new PropertyChangedCallback(OnEnabledChanged)));
private static void OnEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
ListView view = obj as ListView;
if (view != null)
{
bool enabled = (bool)e.NewValue;
if (enabled)
{
view.Loaded += (sender, e2) =>
{
UpdateListView((ListView)sender);
};
view.TargetUpdated += (sender, e2) =>
{
UpdateListView((ListView)sender);
};
view.DataContextChanged += (sender, e2) =>
{
UpdateListView((ListView)sender);
};
}
}
}
}
Entonces, puede ser utilizado como tal:
<ListView foo:GridViewColumnVisibilityManager.Enabled="True">
...
<GridViewColumn Header="Status" foo:GridViewColumnVisibilityManager.IsVisible="{Binding ShowStatusColumn}">
<GridViewColumn.CellTemplate>
<DataTemplate> ...
Este es mi código, funciona muy bien en mi proyecto. Si no te gusta añadir algún código externo.
/// <summary>
/// show/hide datagrid column
/// </summary>
/// <param name="datagrid"></param>
/// <param name="header"></param>
private void ToggleDataGridColumnsVisible()
{
if (IsNeedToShowHideColumn())
{
foreach (GridViewColumn column in ((GridView)(this.ListView1.View)).Columns)
{
GridViewColumnHeader header = column.Header as GridViewColumnHeader;
if (header != null)
{
string headerstring = header.Tag.ToString();
if (!IsAllWaysShowingHeader(headerstring ) )
{
if (IsShowingHeader())
{
}
else
{
//hide it
header.Template = null;
column.CellTemplate = null;
column.Width = 0;
}
}
}
}
}
}
Esto funciona para mí
Necesidad de enlazar la visibilidad tanto en el encabezado como en el contenido.
En este caso es al final, así que no me preocupo por el ancho.
PERO el usuario no obtiene un gancho de interfaz de usuario para restablecer el ancho, por lo que si establece el ancho a cero, desaparecerá.
<GridViewColumn Width="60">
<GridViewColumnHeader HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch"
Visibility="{Binding Source={x:Static Application.Current}, Path=MyGabeLib.CurUser.IsInRoleSysAdmin, Converter={StaticResource bvc}}">
<TextBlock>WS<LineBreak/>Count</TextBlock>
</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=WordScoreCount, StringFormat={}{0:N0}}" HorizontalAlignment="Right"
Visibility="{Binding Source={x:Static Application.Current}, Path=MyGabeLib.CurUser.IsInRoleSysAdmin, Converter={StaticResource bvc}}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Probablemente lo mejor es crear un control personalizado al heredar de la clase GridView , agregar las columnas necesarias y exponer una propiedad significativa para mostrar / ocultar una columna en particular. Tu clase personalizada de GridView podría verse así:
using System;
using System.Windows.Controls;
namespace MyProject.CustomControls
{
public class CustomGridView : GridView
{
private GridViewColumn _fixedColumn;
private GridViewColumn _optionalColumn;
public CustomGridView()
{
this._fixedColumn = new GridViewColumn() { Header = "Fixed Column" };
this._optionalColumn = new GridViewColumn() { Header = "Optional Column" };
this.Columns.Add(_fixedColumn);
this.Columns.Add(_optionalColumn);
}
public bool ShowOptionalColumn
{
get { return _optionalColumn.Width > 0; }
set
{
// When ''False'' hides the entire column
// otherwise its width will be set to ''Auto''
_optionalColumn.Width = (!value) ? 0 : Double.NaN;
}
}
}
}
Entonces, simplemente puede configurar esa propiedad desde XAML como en este ejemplo:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:MyProject.CustomControls"
Title="Window1"
Height="300"
Width="300">
<StackPanel>
<ListView>
<ListView.View>
<cc:CustomGridView ShowOptionalColumn="False" />
</ListView.View>
</ListView>
<ListView>
<ListView.View>
<cc:CustomGridView ShowOptionalColumn="True" />
</ListView.View>
</ListView>
</StackPanel>
</Window>
Opcionalmente, podría hacer que '' CustomGridView.ShowOptionalColumn '' sea una propiedad de dependencia para poder usarla como un destino de enlace.
Tengo una solución mucho más simple que usar un comportamiento adjunto.
Todo lo que tiene que hacer es vincular la propiedad Width de GridViewColumn a un booleano en su ViewModel. Luego crea un convertidor simple como BooleanToWidthConverter que toma un valor booleano y devuelve un doble, cero si es falso, x ancho si es verdadero.
Espero que esto te ayude y te haga la vida más fácil.
XAML:
<GridViewColumn x:Name="MyHiddenGridViewColumn"
Width={Binding Path=IsColumnVisibleProperty, Converter={StaticResource BooleanToWidthConverter}}">
<!-- GridViewColumn.HeaderTemplate etc. goes here. -->
</GridViewColumn>
Convertidor:
public class BooleanToWidthConverter : IValueConverter
{
private const double Column_Width = 40.0;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value != DependencyProperty.UnsetValue)
{
bool isVisible = (bool) value;
return isVisible ? Column_Width : 0;
}
return Column_Width;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Tomado de here
<ListView Grid.Column="1" Grid.Row="1" Name="FicheList" >
<ListView.Resources>
<ResourceDictionary>
<Style x:Key="hiddenStyle" TargetType="GridViewColumnHeader">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</ResourceDictionary>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Code}" Header="Code" Width="0" HeaderContainerStyle="{StaticResource hiddenStyle}" />
<GridViewColumn DisplayMemberBinding="{Binding FicheTitle}" Header="Title" Width="100" />
<GridViewColumn DisplayMemberBinding="{Binding CategoryName}" Header="Category" Width="100" />
<GridViewColumn DisplayMemberBinding="{Binding UpdateDate}" Header="Update Date" Width="100" />
</GridView>
</ListView.View>
</ListView>
Yo sugeriría usar una propiedad personalizada (o secuestrar una existente) en el padre y luego usar un estilo personalizado en el gridviewcolumnheader para hacer referencia a esa propiedad ancestral. Me gusta esto:
<Window.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=Tag}"/>
</Style>
<GridView x:Key="myGridView" x:Shared="false">
<GridViewColumn Header="Created" DisplayMemberBinding="{Binding Path=Created}"/>
</GridView>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel>
<ListView x:Name="detailList" View="{StaticResource myGridView}"/>
<ListView x:Name="detailListHide" Tag="{x:Static Member=Visibility.Hidden}" View="{StaticResource myGridView}"/>
</StackPanel>
</Grid>
En una pequeña utilidad que escribí , tengo una vista de lista donde el usuario puede ocultar / mostrar algunas columnas. No hay propiedad de visibilidad en las columnas, por lo que decidí establecer el ancho de las columnas ocultas en cero. No es lo ideal, ya que el usuario todavía puede cambiar su tamaño y hacerlos visibles de nuevo.
De todos modos, para hacer esto, utilicé:
<GridViewColumn.Width>
<MultiBinding Converter="{StaticResource WidthConverter}" Mode="TwoWay">
<Binding Path="ThreadIdColumnWidth" Mode="TwoWay" />
<Binding Path="IsThreadIdShown" />
</MultiBinding>
</GridViewColumn.Width>
IsThreadIdShown
está vinculado a una casilla de verificación en la barra de herramientas. Y el convertidor multivalor es:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
if (values.Length != 2) {
return null;
}
object o0 = values[0];
object o1 = values[1];
if (! (o1 is bool)) {
return o0;
}
bool toBeDisplayed = (bool) o1;
if (! toBeDisplayed) {
return 0.0;
}
if (! (o0 is double)) {
return 0;
}
return (double) o0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
return new object[] { (double)value, Binding.DoNothing };
}