Related
Why does my TreeViewItem does not wrap in my sample code?
Xaml:
<Window x:Class="WpfAppTestScrollViewBehavior.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfAppTestScrollViewBehavior"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowModel></local:MainWindowModel>
</Window.DataContext>
<TabControl>
<TabItem Header="Test">
<TreeView ItemsSource="{Binding Level1s, Mode=OneWay}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Background="LightGoldenrodYellow">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Level1}"
ItemsSource="{Binding Path=InstalledFontCollection}">
<Grid HorizontalAlignment="Stretch" Background="LightGreen">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" IsChecked="{Binding Path=IsSelected}"></CheckBox>
<TextBlock Grid.Column="1" Text="{Binding Path=Name}"
TextWrapping="Wrap" Margin="5,0,0,0"></TextBlock>
</Grid>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</TabItem>
</TabControl>
</Window>
Code:
using System;
using System.Collections.Generic;
namespace WpfAppTestScrollViewBehavior
{
public class MainWindowModel
{
public List<Level1> Level1s { get; } = new List<Level1>();
public MainWindowModel()
{
Level1s.Add(new Level1());
}
}
}
using System;
using System.Drawing.Text;
namespace WpfAppTestScrollViewBehavior
{
public class Level1
{
public bool IsSelected { get; set; }
public string Name { get; set; } = "a very long name in order to test text wrapping functionnality";
public InstalledFontCollection InstalledFontCollection { get; } = new InstalledFontCollection();
}
}
Just to prevent quick wrong answers:
You can add this code and it works fine:
<TabItem Header="Test 2">
<ScrollViewer HorizontalScrollBarVisibility="Disabled">
<Grid HorizontalAlignment="Stretch" Background="LightPink">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0"></CheckBox>
<TextBlock Grid.Column="1" Text="long name just for testing"
TextWrapping="Wrap" Margin="5,0,0,0"></TextBlock>
</Grid>
</ScrollViewer>
</TabItem>
Results of previous working code (just as example of what i expect):
Since you have not specified Width it is giving available Width to the <Textblock>
Why does my TreeViewItem does not wrap in my sample code?
The text is being wrapped but because of unrestricted width available wrap is not seen
For example:
XAML: Width: 150 units TextWrapping = "NoWrap"
<TextBlock Grid.Column="1" Text="{Binding Path=Name}" Width="150" Name="textblock1"
TextWrapping="NoWrap" Margin="10,0,0,0"></TextBlock>
OUTPUT:
XAML: Width: 150 units TextWrapping = "Wrap"
<TextBlock Grid.Column="1" Text="{Binding Path=Name}" Width="150" Name="textblock1"
TextWrapping="Wrap" Margin="10,0,0,0"></TextBlock>
OR
<Grid HorizontalAlignment="Stretch" Width="150" Background="LightGreen">
public string Name { get; set; } = ;
ColumnDefinition defaults to * so the TextBlock in column 1 will take all available space.
Try experimenting with MaxWidth like this: <ColumnDefinition Width="Auto" MaxWidth="100"/>
EDIT
If you don't want to use predefined Width or MaxWidth, bind the Width of the Grid to ActualWidth of TabControl like this:
<Grid Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=ActualWidth}" />
I just tested this approach and it wrapped the TextBlock fine and re-wrapped it as I resized the window... If the Text of the TextBlock has no margin relative to the right window border when Width is set this way you can fix that by setting the right margin of the TextBlock to 10 or more depending on the desired padding...
I came up with a solution that is generic and seems to works for any cases whatever content is inside TreeViewItem.
It could be applied as a behavior (I favor this in order to make it simpler to use).
One restriction of using this behavior is: Define the behavior as the last element in the TreeView xaml. By doing so it will support any additional ItemContainerStyle previously defined.
Behavior code:
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HQ.Wpf.Util;
using HQ.Wpf.Util.Converter;
namespace WpfAppTestScrollViewBehavior
{
public class BehaviorTreeView : Behavior<TreeView>
{
public static readonly DependencyProperty ShowHorizontalScrollBarProperty = DependencyProperty.Register(
"PropertyType", typeof(bool), typeof(BehaviorTreeView), new PropertyMetadata(true));
/// <summary>
/// Settting this poroperty to false will fake a TreeView-ScrollView ViewPort finite width.
/// TreeViewItem will be advise to Calculate their width according to the width of the
/// ViewPort of the ScrollView of the TreeView.
/// </summary>
public bool ShowHorizontalScrollBar
{
get { return (bool)GetValue(ShowHorizontalScrollBarProperty); }
set { SetValue(ShowHorizontalScrollBarProperty, value); }
}
// ******************************************************************
protected override void OnAttached()
{
base.OnAttached();
if (!ShowHorizontalScrollBar)
{
Style style = AssociatedObject.ItemContainerStyle ?? new Style(typeof(TreeViewItem));
var eventSetter = new EventSetter(FrameworkElement.LoadedEvent, new RoutedEventHandler(this.TreeViewItemLoaded));
style.Setters.Add(eventSetter);
AssociatedObject.ItemContainerStyle = style;
}
}
// ******************************************************************
private void TreeViewItemLoaded(object sender, RoutedEventArgs e)
{
var tvi = sender as TreeViewItem;
var contentPresenter = tvi.FindFirstChildWithNameRecursive<ContentPresenter>("PART_Header");
var treeView = tvi.GetVisualParentRecursive<TreeView>();
double offsetX = contentPresenter.TransformToAncestor(treeView).Transform(new Point(0, 0)).X;
var scrollViewer = treeView.FindVisualChild<ScrollViewer>();
Binding binding = new Binding();
binding.Source = scrollViewer;
binding.Path = new PropertyPath(nameof(ScrollViewer.ViewportWidth));
binding.Mode = BindingMode.OneWay;
var converter = new NumericFixedNumberAddedConverter();
binding.Converter = converter;
binding.ConverterParameter = -offsetX;
BindingOperations.SetBinding(contentPresenter, FrameworkElement.WidthProperty, binding);
}
// ******************************************************************
}
}
Behavior Dependencies:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace WpfAppTestScrollViewBehavior
{
class NumericFixedNumberAddedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return (double) value + double.Parse(parameter.ToString());
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace WpfAppTestScrollViewBehavior
{
public static class UiUtility
{
// ******************************************************************
public static Window GetTopLevelOwnerWindowOrMainAppWindow(Control usercontrol)
{
return GetTopLevelOwnerWindow(usercontrol) ?? Application.Current.MainWindow;
}
// ******************************************************************
public static Window GetTopLevelOwnerWindowOrMainAppWindow(DependencyObject dependencyObject)
{
return GetTopLevelOwnerWindow(dependencyObject) ?? Application.Current.MainWindow;
}
// ******************************************************************
public static Window GetTopLevelOwnerWindow(Control usercontrol)
{
return GetTopLevelOwnerWindow((DependencyObject)usercontrol);
}
// ******************************************************************
public static Window GetTopLevelOwnerWindow(DependencyObject dependencyObject)
{
while (dependencyObject != null && !(dependencyObject is Window))
{
var dependencyObjectCopy = dependencyObject;
dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
if (dependencyObject == null)
{
dependencyObject = dependencyObjectCopy;
String propName = "DockSite";
PropertyInfo pi = dependencyObject.GetType().GetProperty(propName);
if (pi != null)
{
DependencyObject dependencyObjectTemp = null;
try
{
dependencyObjectTemp = dependencyObject.GetType().InvokeMember(propName,
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public, null, dependencyObject, null) as DependencyObject;
}
catch (Exception)
{
}
if (dependencyObjectTemp != null)
{
dependencyObject = LogicalTreeHelper.GetParent(dependencyObjectTemp);
}
else
{
dependencyObject = LogicalTreeHelper.GetParent(dependencyObject);
}
}
else
{
dependencyObject = LogicalTreeHelper.GetParent(dependencyObject);
}
}
}
return dependencyObject as Window;
}
// ******************************************************************
public static T FindVisualParent<T>(DependencyObject element) where T : DependencyObject
{
var parent = element;
while (parent != null)
{
var correctlyTyped = parent as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
parent = VisualTreeHelper.GetParent(parent) as DependencyObject;
}
return null;
}
// ******************************************************************
public static bool IsParentOf(DependencyObject parent, DependencyObject child)
{
if (parent == null || child == null)
{
return false;
}
DependencyObject childParent = child;
do
{
if (childParent == parent)
{
return true;
}
childParent = VisualTreeHelper.GetParent(childParent);
} while (childParent != null);
return false;
}
// ******************************************************************
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
// ******************************************************************
public static DataGridColumnHeader GetColumnHeaderFromColumn(DataGrid dataGrid, DataGridColumn column)
{
// dataGrid is the name of your DataGrid. In this case Name="dataGrid"
foreach (var columnHeader in FindVisualChildren<DataGridColumnHeader>(dataGrid))
{
if (columnHeader.Column == column)
{
return columnHeader;
}
}
return null;
}
// ******************************************************************
public static void SafeInvoke(Action action)
{
if (Application.Current.Dispatcher.CheckAccess())
{
action();
}
else
{
Application.Current.Dispatcher.Invoke(action);
}
}
// ******************************************************************
public static void SafeBeginInvoke(Action action)
{
if (Application.Current.Dispatcher.CheckAccess())
{
action();
}
else
{
Application.Current.Dispatcher.BeginInvoke(action);
}
}
// ******************************************************************
public static void BindingRefresh(DependencyObject dependencyObject, DependencyProperty dependencyProperty)
{
BindingExpressionBase b = BindingOperations.GetBindingExpressionBase(dependencyObject, dependencyProperty);
if (b != null)
{
b.UpdateTarget();
}
}
// ******************************************************************
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(DependencyObject depObj, string childName)
where T : DependencyObject
{
// Confirm obj is valid.
if (depObj == null) return null;
// success case
if (depObj is T && ((FrameworkElement)depObj).Name == childName)
return depObj as T;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
//DFS
T obj = FindChild<T>(child, childName);
if (obj != null)
return obj;
}
return null;
}
// ******************************************************************
public static void DebugPrintControlParentHierarchy(object frameworkElement)
{
StringBuilder hierarchy = new StringBuilder();
var fe = frameworkElement as FrameworkElement;
while (fe != null)
{
hierarchy.Append(String.Format("{0} [{1}] ==> ", fe.GetType(), fe.Name));
fe = VisualTreeHelper.GetParent(fe) as FrameworkElement;
}
hierarchy.Append("!TOP!");
Debug.Print(hierarchy.ToString());
}
// ******************************************************************
/// <summary>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T FindVisualChild<T>(this DependencyObject obj) where T : DependencyObject
{
if (obj != null && obj is Visual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
var visualChild = child as T;
if (visualChild != null)
return visualChild;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
}
return null;
}
// ******************************************************************
public static FrameworkElement GetVisualParent(this UIElement element)
{
DependencyObject parent = VisualTreeHelper.GetParent(element);
do
{
var fe = parent as FrameworkElement;
if (fe != null)
{
return fe;
}
parent = VisualTreeHelper.GetParent(parent);
} while (parent != null);
return null;
}
// ******************************************************************
public static void BringToFront(this Panel panel, UIElement element)
{
int maxIndex = 0;
foreach (UIElement e in panel.Children)
{
maxIndex = Math.Max(maxIndex, Panel.GetZIndex(e));
}
Panel.SetZIndex(element, maxIndex + 1);
}
//// ******************************************************************
///// <summary>
///// Return the center point of an direct child of a Canvas (not yet tested)
///// </summary>
///// <param name=""></param>
///// <param name="elementRelativeTo">If elementRelativeTo == null, will use direct parent</param>
///// <returns></returns>
//public static Point GetCanvasElementCenterPoint(this FrameworkElement element)
//{
// return new Point(
// Canvas.GetLeft(element) + (element.ActualWidth / 2),
// Canvas.GetTop(element) + (element.ActualHeight / 2));
//}
// ******************************************************************
public enum PointPositionVertical
{
Top,
Center,
Bottom
}
// ******************************************************************
public enum PointPositionHorizontal
{
Left,
Center,
Right
}
// ******************************************************************
public static Point GetChildCoordinate(this UIElement elementContainer, FrameworkElement childElement,
PointPositionHorizontal pointPositionHorizontal = PointPositionHorizontal.Left,
PointPositionVertical pointPositionVertical = PointPositionVertical.Top)
{
double x;
switch (pointPositionHorizontal)
{
case PointPositionHorizontal.Center:
x = childElement.ActualWidth / 2;
break;
case PointPositionHorizontal.Right:
x = childElement.ActualWidth;
break;
default:
x = 0;
break;
}
double y;
switch (pointPositionVertical)
{
case PointPositionVertical.Center:
y = childElement.ActualHeight / 2;
break;
case PointPositionVertical.Bottom:
y = childElement.ActualHeight;
break;
default:
y = 0;
break;
}
return childElement.TranslatePoint(new Point(x, y), elementContainer);
}
// ******************************************************************
public static void ApplyToEachVisualChildRecursively(this DependencyObject obj, Action<DependencyObject> action)
{
if (obj != null && obj is Visual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null)
{
action(child);
ApplyToEachVisualChildRecursively(child, action);
}
}
}
}
// ******************************************************************
public static T GetVisualParentRecursive<T>(this DependencyObject obj) where T : class
{
var element = obj as FrameworkElement;
if (element != null)
{
var frameWorkElement = VisualTreeHelper.GetParent(element) as FrameworkElement;
if (frameWorkElement != null)
{
var t = frameWorkElement as T;
if (t != null)
{
return t;
}
return frameWorkElement.GetVisualParentRecursive<T>();
}
}
return null;
}
// ******************************************************************
public static T HitTest<T>(this Visual visual, Point pt) where T : class
{
T hitResult = null;
VisualTreeHelper.HitTest(visual, null, result =>
{
if (result.VisualHit is T)
{
hitResult = result.VisualHit as T;
return HitTestResultBehavior.Stop;
}
hitResult = result.VisualHit?.GetVisualParentRecursive<T>();
if (hitResult != null)
{
return HitTestResultBehavior.Stop;
}
return HitTestResultBehavior.Continue;
}, new PointHitTestParameters(pt));
return hitResult;
}
// ******************************************************************
public static IEnumerable<T> GetChildrenRecursive<T>(this DependencyObject depObj) where T : class
{
int count = VisualTreeHelper.GetChildrenCount(depObj);
for (int n = 0; n < count; n++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, n);
if (child is T)
{
yield return child as T;
}
foreach (T depObjChild in child.GetChildrenRecursive<T>())
{
yield return depObjChild;
}
}
}
// ******************************************************************
/// <summary>
/// EO, 2017-05-11: New code
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static T GetVisualParentRecursive<T>(this DependencyObject obj, Predicate<T> predicate = null) where T : class
{
var element = obj as FrameworkElement;
if (element != null)
{
var frameWorkElement = VisualTreeHelper.GetParent(element) as FrameworkElement;
if (frameWorkElement != null)
{
var t = frameWorkElement as T;
if (t != null)
{
if (predicate == null || predicate(t))
{
return t;
}
}
return frameWorkElement.GetVisualParentRecursive<T>(predicate);
}
}
return null;
}
// ******************************************************************
/// <summary>
/// EO, 2017-05-11: New code
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent"></param>
/// <param name="name"></param>
/// <returns></returns>
public static T FindFirstChildWithNameRecursive<T>(this DependencyObject parent, string name) where T : FrameworkElement
{
return FindFirstChildRecursive(parent, (T child) => child.Name == name);
}
// ******************************************************************
/// <summary>
/// Find all controls (visual or not)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static T FindFirstChildRecursive<T>(this DependencyObject parent, Predicate<T> predicate = null) where T : DependencyObject
{
if (parent == null)
{
return null;
}
//use the visual tree for Visual / Visual3D elements
if (parent is Visual || parent is Visual3D)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var childAsT = child as T;
if (childAsT != null)
{
if (predicate == null || predicate(childAsT))
{
return childAsT;
}
}
var result = FindFirstChildRecursive(child, predicate);
if (result != null)
{
return result;
}
}
}
else //use the logical tree for content / framework elements
{
foreach (DependencyObject child in LogicalTreeHelper.GetChildren(parent))
{
var childAsT = child as T;
if (childAsT != null)
{
if (predicate == null || predicate(childAsT))
{
return childAsT;
}
}
var result = FindFirstChildRecursive(child, predicate);
if (result != null)
{
return result;
}
}
}
return null;
}
// ******************************************************************
/// <summary>
/// Non recursive
/// Based on stackoverflow: http://stackoverflow.com/questions/13248013/visualtreehelper-not-finding-children-of-dependencyobject-how-can-i-reliably-fi
/// Find all controls (visual or not)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static IEnumerable<T> FindChilds<T>(this DependencyObject parent, Predicate<T> predicate) where T : DependencyObject
{
if (parent == null) yield break;
//use the visual tree for Visual / Visual3D elements
if (parent is Visual || parent is Visual3D)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
var childAsT = VisualTreeHelper.GetChild(parent, i) as T;
if (childAsT != null)
{
if (predicate(childAsT))
{
yield return childAsT;
}
}
}
}
else //use the logical tree for content / framework elements
{
foreach (DependencyObject obj in LogicalTreeHelper.GetChildren(parent))
{
var childAsT = obj as T;
if (childAsT != null)
{
if (predicate(childAsT))
{
yield return childAsT;
}
}
}
}
}
// ******************************************************************
/// <summary>
/// EO, 2017-05-11: New code
/// Find all controls (visual or not)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent"></param>
/// <param name="name"></param>
/// <returns></returns>
public static List<T> FindChildsWithNameRecursive<T>(this DependencyObject parent, string name) where T : FrameworkElement
{
return FindChildsRecursive(parent, (T child) => child.Name == name);
}
// ******************************************************************
/// <summary>
/// Find all controls (visual or not)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static List<T> FindChildsRecursive<T>(this DependencyObject parent, Predicate<T> predicate = null) where T : DependencyObject
{
List<T> childs = new List<T>();
return FindChildsRecursiveInternal(parent, predicate, childs);
}
// ******************************************************************
private static List<T> FindChildsRecursiveInternal<T>(this DependencyObject parent, Predicate<T> predicate, List<T> childs) where T : DependencyObject
{
if (parent != null)
{
//use the visual tree for Visual / Visual3D elements
if (parent is Visual || parent is Visual3D)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var childAsT = child as T;
if (childAsT != null)
{
if (predicate == null || predicate(childAsT))
{
childs.Add(childAsT);
}
}
FindChildsRecursiveInternal(child, predicate, childs);
}
}
else //use the logical tree for content / framework elements
{
foreach (DependencyObject child in LogicalTreeHelper.GetChildren(parent))
{
var childAsT = child as T;
if (childAsT != null)
{
if (predicate == null || predicate(childAsT))
{
childs.Add(childAsT);
}
}
FindChildsRecursiveInternal(child, predicate, childs);
}
}
}
return childs;
}
// ****************************************************************** }
}
}
Usage:
<!-- ScrollViewer.HorizontalScrollBarVisibility="Disabled" -->
<TreeView ItemsSource="{Binding Level1s, Mode=OneWay}">
...
<i:Interaction.Behaviors>
<local:BehaviorTreeView ShowHorizontalScrollBar="False"></local:BehaviorTreeView>
</i:Interaction.Behaviors>
Thanks to Dean Kuga. Its solution give me the start to what I came up with.
Results:
How to clean the previously selected row ?
I have this method that search for item with a given id.
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
foreach (MessageFieldViewModel rowItem in Datagrid.ItemsSource)
{
if (_mainWindowModel != null)
_mainWindowModel.SelectedMessageElement = (MessageElementViewModel)e.NewValue;
var row = Datagrid.ItemContainerGenerator.ContainerFromItem(rowItem) as DataGridRow;
if (_mainWindowModel != null && _mainWindowModel.SelectedMessageElement != null)
{
if (rowItem.Id == _mainWindowModel.SelectedMessageElement.Id)
{
if (row != null)
row.Background = Brushes.DarkSalmon;
}
row.Background.ClearValue();
}
// if (item != null) row.Background.ClearValue(rowItem.Id);
// break;
}
This selects the row of a given id. but if I want to select another Id The previous id is still selected.
how can I remove previously selected Ids and show only the newly selected id?
ok so here we go
XAML
<TreeView Name="Tree" Width="50" Height="100"
ItemsSource="{Binding YourTree}"
SelectedItemChanged="Tree_SelectedItemChanged"
SelectedValuePath="yourIdProperty"/>
<DataGrid Name="myDatagrid" Width="100" Height="100"
ItemsSource="{Binding YourList}"
SelectedItem="{Binding YourSelectedItem,UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Id"/>
Codebehind
private void Tree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var tree = sender as TreeView;
myDatagrid.SelectedValue = tree.SelectedValue;
}
ViewModel
public List<yourClass> YourList { get; set; }
private yourClass yourSelectedItem;
public yourClass YourSelectedItem
{
get { return yourSelectedItem; }
set
{
yourSelectedItem = value;
OnPropertyChanged("YourSelectedItem");
}
}
you need to set the SelectedValuePath based your Property which represent your Id but not as Binding just as string as example "Id"
I need to create tiles of well formatted buttons, something like the Windows 8 start page. Is there any toolkit available for a custom ListView which may support tile view or grid view, with some formatting and may be some animation options.
I tried creating my own custom listview but it seemed to be a complicated task.
I am not aware of a nice free tile control. DevExpress has a nice looking commercial version.
If you'd specify your exact requirements (i.e. what properties do you need configurable, what kind of animation,...) and I find the time, I would give it a whirl though.
EDIT: I've created an ItemsControl with a WrapPanel as ItemsPanel. Using the MVVM pattern, expanding the controls to your needs and your data objects should not be too difficult. Rather hard to do is the DragDrop behavior part - there certainly is still some room for improvement. I didn't include the images.
TileControl.xaml:
<UserControl x:Class="WpfApplication1.TileControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:beh="clr-namespace:WpfApplication1.Behavior"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:ViewModel />
</UserControl.DataContext>
<UserControl.Resources>
<local:TileTypeToColorConverter x:Key="TileTypeToColorConverter" />
</UserControl.Resources>
<Grid>
<Image Source="/WpfApplication1;component/Themes/background.png" Stretch="UniformToFill" />
<Border x:Name="darkenBorder" Background="Black" Opacity="0.6" />
<ItemsControl ItemsSource="{Binding Tiles}" Background="Transparent" Margin="5">
<i:Interaction.Behaviors>
<beh:ItemsControlDragDropBehavior />
</i:Interaction.Behaviors>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:TileModel">
<Button Content="{Binding Text}" Background="{Binding TileType, Converter={StaticResource TileTypeToColorConverter}}"
Command="{Binding ClickCommand}" Width="120" Height="110" Padding="5" RenderTransformOrigin="0.5, 0.5" >
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Padding="5" Background="Transparent">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border x:Name="tileBackground" Grid.RowSpan="2" Background="{TemplateBinding Background}" Opacity="0.9" />
<Image Source="{Binding Image}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="50" />
<ContentPresenter TextElement.Foreground="White" Grid.Row="1" HorizontalAlignment="Center" Margin="3,10" />
</Grid>
</Border>
</ControlTemplate>
</Button.Template>
<Button.Resources>
<ElasticEase x:Key="easeOutBounce" EasingMode="EaseOut" Springiness="6" Oscillations="4" />
</Button.Resources>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard Duration="00:00:00.05" AutoReverse="True">
<DoubleAnimation To="0.1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"/>
<DoubleAnimation To="0.1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" From="0.1" To="1.0" EasingFunction="{StaticResource easeOutBounce}" />
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" From="0.1" To="1.0" EasingFunction="{StaticResource easeOutBounce}" />
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.1" To="1.0" EasingFunction="{StaticResource easeOutBounce}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
ViewModel, TileModel, TileType, ActionCommand:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using System.Windows.Media.Imaging;
namespace WpfApplication1
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<TileModel> _tiles;
public ObservableCollection<TileModel> Tiles { get { return _tiles; } set { _tiles = value; OnPropertyChanged("Tiles"); } }
public ViewModel()
{
Tiles= new ObservableCollection<TileModel>()
{
new TileModel() { Text = "Facebook", Image = Properties.Resources.Facebook.ToBitmapImage(), TileType = TileType.Website },
new TileModel() { Text = "Skype", Image = Properties.Resources.Skype.ToBitmapImage(), TileType = TileType.Application },
new TileModel() { Text = "Ask.com", Image = Properties.Resources.AskCom.ToBitmapImage(), TileType = TileType.Website },
new TileModel() { Text = "Amazon", Image = Properties.Resources.Amazon.ToBitmapImage(), TileType = TileType.Website },
new TileModel() { Text = "Evernote", Image = Properties.Resources.Evernote.ToBitmapImage(), TileType = TileType.Application },
new TileModel() { Text = "Twitter", Image = Properties.Resources.Twitter.ToBitmapImage(), TileType = TileType.Website },
new TileModel() { Text = "Internet Explorer", Image = Properties.Resources.InterneExplorer.ToBitmapImage(), TileType = TileType.Browser },
new TileModel() { Text = "Android", Image = Properties.Resources.Android.ToBitmapImage(), TileType = TileType.Application },
new TileModel() { Text = "Winamp", Image = Properties.Resources.Winamp.ToBitmapImage(), TileType = TileType.Application },
new TileModel() { Text = "YouTube", Image = Properties.Resources.YouTube.ToBitmapImage(), TileType = TileType.Website },
};
}
}
public class TileModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _text;
public string Text { get { return _text; } set { _text = value; OnPropertyChanged("Text"); } }
private BitmapSource _image;
public BitmapSource Image { get { return _image; } set { _image = value; OnPropertyChanged("Image"); } }
private TileType _tileType;
public TileType TileType { get { return _tileType; } set { _tileType = value; OnPropertyChanged("TileType"); } }
public ICommand ClickCommand { get; private set; }
public TileModel()
{
ClickCommand = new ActionCommand(Click);
}
private void Click()
{
// execute appropriate action
}
}
public enum TileType
{
Browser,
Website,
Application
}
public class ActionCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private Action _action;
public ActionCommand(Action action)
{
_action = action;
}
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter)
{
if (_action != null)
_action();
}
}
}
ItemsControlDragDropBehavior:
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;
namespace WpfApplication1.Behavior
{
public class ItemsControlDragDropBehavior : Behavior<ItemsControl>
{
private bool _isMouseDown;
private bool _isDragging;
private Point _dragStartPosition;
private UIElement _dragItem;
private UIElement _dragContainer;
private IDataObject _dataObject;
private int _currentDropIndex;
private Point _lastCheckPoint;
protected override void OnAttached()
{
this.AssociatedObject.AllowDrop = true;
this.AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
this.AssociatedObject.PreviewMouseMove += AssociatedObject_PreviewMouseMove;
this.AssociatedObject.PreviewDragOver += AssociatedObject_PreviewDragOver;
this.AssociatedObject.PreviewDrop += AssociatedObject_PreviewDrop;
this.AssociatedObject.PreviewMouseLeftButtonUp += AssociatedObject_PreviewMouseLeftButtonUp;
base.OnAttached();
}
void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ItemsControl itemsControl = (ItemsControl)sender;
Point p = e.GetPosition(itemsControl);
object data = itemsControl.GetDataObjectFromPoint(p);
_dataObject = data != null ? new DataObject(data.GetType(), data) : null;
_dragContainer = itemsControl.GetItemContainerFromPoint(p);
if (_dragContainer != null)
_dragItem = GetItemFromContainer(_dragContainer);
if (data != null)
{
_isMouseDown = true;
_dragStartPosition = p;
}
}
void AssociatedObject_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_isMouseDown)
{
ItemsControl itemsControl = (ItemsControl)sender;
Point currentPosition = e.GetPosition(itemsControl);
if ((_isDragging == false) && (Math.Abs(currentPosition.X - _dragStartPosition.X) > SystemParameters.MinimumHorizontalDragDistance) ||
(Math.Abs(currentPosition.Y - _dragStartPosition.Y) > SystemParameters.MinimumVerticalDragDistance))
{
DragStarted(e.GetPosition(itemsControl));
}
e.Handled = true;
}
}
void AssociatedObject_PreviewDragOver(object sender, DragEventArgs e)
{
UpdateDropIndex(e.GetPosition(this.AssociatedObject));
}
void AssociatedObject_PreviewDrop(object sender, DragEventArgs e)
{
UpdateDropIndex(e.GetPosition(this.AssociatedObject));
}
void AssociatedObject_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_isMouseDown = false;
}
private void DragStarted(Point p)
{
if (!_isDragging)
{
_isDragging = true;
if (_dragContainer != null)
_dragContainer.Opacity = 0.3;
_currentDropIndex = FindDropIndex(p);
DragDropEffects e = DragDrop.DoDragDrop(this.AssociatedObject, _dataObject, DragDropEffects.Copy | DragDropEffects.Move);
ResetState();
}
}
private void ResetState()
{
if (_dragContainer != null)
_dragContainer.Opacity = 1.0;
_isMouseDown = false;
_isDragging = false;
_dataObject = null;
_dragItem = null;
_dragContainer = null;
_currentDropIndex = -1;
}
private void UpdateDropIndex(Point p)
{
if ((_lastCheckPoint - p).Length > SystemParameters.MinimumHorizontalDragDistance) // prevent too frequent call
{
int dropIndex = FindDropIndex(p);
if (dropIndex != _currentDropIndex && dropIndex > -1)
{
this.AssociatedObject.RemoveItem(_dataObject);
this.AssociatedObject.AddItem(_dataObject, dropIndex);
_currentDropIndex = dropIndex;
}
_lastCheckPoint = p;
}
}
private int FindDropIndex(Point p)
{
ItemsControl itemsControl = this.AssociatedObject;
UIElement dropTargetContainer = null;
dropTargetContainer = itemsControl.GetItemContainerFromPoint(p);
int index = -1;
if (dropTargetContainer != null)
{
index = itemsControl.ItemContainerGenerator.IndexFromContainer(dropTargetContainer);
if (!IsPointInTopHalf(p))
index = index++; // in second half of item, add after
}
else if (IsPointAfterAllItems(itemsControl, p))
{
// still within itemscontrol, but after all items
index = itemsControl.Items.Count - 1;
}
return index;
}
public bool IsPointInTopHalf(Point p)
{
ItemsControl itemsControl = this.AssociatedObject;
bool isInTopHalf = false;
UIElement selectedItemContainer = itemsControl.GetItemContainerFromPoint(p);
Point relativePosition = Mouse.GetPosition(selectedItemContainer);
if (IsItemControlOrientationHorizontal())
isInTopHalf = relativePosition.X < ((FrameworkElement)selectedItemContainer).ActualWidth / 2;
else
isInTopHalf = relativePosition.Y < ((FrameworkElement)selectedItemContainer).ActualHeight / 2;
return isInTopHalf;
}
private bool IsItemControlOrientationHorizontal()
{
bool isHorizontal = false;
Panel panel = GetItemsPanel();
if (panel is WrapPanel)
isHorizontal = ((WrapPanel)panel).Orientation == Orientation.Horizontal;
else if (panel is StackPanel)
isHorizontal = ((StackPanel)panel).Orientation == Orientation.Horizontal;
return isHorizontal;
}
private UIElement GetItemFromContainer(UIElement container)
{
UIElement item = null;
if (container != null)
item = VisualTreeHelper.GetChild(container, 0) as UIElement;
return item;
}
private Panel GetItemsPanel()
{
ItemsPresenter itemsPresenter = GetVisualChild<ItemsPresenter>(this.AssociatedObject);
Panel itemsPanel = VisualTreeHelper.GetChild(itemsPresenter, 0) as Panel;
return itemsPanel;
}
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
/// still needs some work
private static bool IsPointAfterAllItems(ItemsControl itemsControl, Point point)
{
bool isAfter = false;
UIElement target = itemsControl.GetLastItemContainer();
Point targetPos = target.TransformToAncestor(itemsControl).Transform(new Point(0, 0));
Point relativeToTarget = new Point(point.X - targetPos.X, point.Y - targetPos.Y);
if (relativeToTarget.X >= 0 && relativeToTarget.Y >= 0)
{
var bounds = VisualTreeHelper.GetDescendantBounds(target);
isAfter = !bounds.Contains(relativeToTarget);
}
return isAfter;
}
}
public static class ItemsControlExtensions
{
public static object GetDataObjectFromPoint(this ItemsControl itemsControl, Point p)
{
UIElement element = itemsControl.InputHitTest(p) as UIElement;
while (element != null)
{
if (element == itemsControl)
return null;
object data = itemsControl.ItemContainerGenerator.ItemFromContainer(element);
if (data != DependencyProperty.UnsetValue)
return data;
else
element = VisualTreeHelper.GetParent(element) as UIElement;
}
return null;
}
public static UIElement GetItemContainerFromPoint(this ItemsControl itemsControl, Point p)
{
UIElement element = itemsControl.InputHitTest(p) as UIElement;
while (element != null)
{
object data = itemsControl.ItemContainerGenerator.ItemFromContainer(element);
if (data != DependencyProperty.UnsetValue)
return element;
else
element = VisualTreeHelper.GetParent(element) as UIElement;
}
return element;
}
public static UIElement GetLastItemContainer(this ItemsControl itemsControl)
{
UIElement container = null;
if (itemsControl.HasItems)
container = itemsControl.GetItemContainerAtIndex(itemsControl.Items.Count - 1);
return container;
}
public static UIElement GetItemContainerAtIndex(this ItemsControl itemsControl, int index)
{
UIElement container = null;
if (itemsControl != null && itemsControl.Items.Count > index && itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
container = itemsControl.ItemContainerGenerator.ContainerFromIndex(index) as UIElement;
else
container = itemsControl;
return container;
}
public static void AddItem(this ItemsControl itemsControl, IDataObject item, int insertIndex)
{
if (itemsControl.ItemsSource != null)
{
foreach (string format in item.GetFormats())
{
object data = item.GetData(format);
IList iList = itemsControl.ItemsSource as IList;
if (iList != null)
iList.Insert(insertIndex, data);
else
{
Type type = itemsControl.ItemsSource.GetType();
Type genericList = type.GetInterface("IList`1");
if (genericList != null)
type.GetMethod("Insert").Invoke(itemsControl.ItemsSource, new object[] { insertIndex, data });
}
}
}
else
itemsControl.Items.Insert(insertIndex, item);
}
public static void RemoveItem(this ItemsControl itemsControl, IDataObject itemToRemove)
{
if (itemToRemove != null)
{
foreach (string format in itemToRemove.GetFormats())
{
object data = itemToRemove.GetData(format);
int index = itemsControl.Items.IndexOf(data);
if (index > -1)
itemsControl.RemoveItemAt(index);
}
}
}
public static void RemoveItemAt(this ItemsControl itemsControl, int removeIndex)
{
if (removeIndex != -1 && removeIndex < itemsControl.Items.Count)
{
if (itemsControl.ItemsSource != null)
{
IList iList = itemsControl.ItemsSource as IList;
if (iList != null)
{
iList.RemoveAt(removeIndex);
}
else
{
Type type = itemsControl.ItemsSource.GetType();
Type genericList = type.GetInterface("IList`1");
if (genericList != null)
type.GetMethod("RemoveAt").Invoke(itemsControl.ItemsSource, new object[] { removeIndex });
}
}
else
itemsControl.Items.RemoveAt(removeIndex);
}
}
}
}
TileTypeToColorConverter:
public class TileTypeToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush = new SolidColorBrush();
TileType type = (TileType)value;
switch (type)
{
case TileType.Browser: brush.Color = Colors.Maroon; break;
case TileType.Application: brush.Color = Colors.DodgerBlue; break;
case TileType.Website: brush.Color = Colors.DarkGoldenrod; break;
}
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can simply use Item Control,
i). In Item Panel just give the number of rows and columns you want
ii). If button you want here are to be generated dynamically just assign the list of Buttons to it.
<ItemsControl x:Name="lstButtons"
Grid.Row="0"
Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4"
Rows="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Click="CLICK_EVENT_HERE"
Style="Use metro Button Style here" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have a list of hyperlinks that are displayed through an ItemsControl, something like this:
<ItemsControl x:Name="SubMenu" Visibility="Collapsed">
<ItemsControl.ItemTemplate>
<DataTemplate>
<HyperlinkButton Content="{Binding Name}"
NavigateUri="{Binding Url}"
TargetName="ContentFrame"
Style="{StaticResource LinkStyle}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Style="{StaticResource LinksStackPanelStyle}"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
what I need to do is enumerate the actual hyperlinks in the submenu, something like this:
foreach (UIElement child in SubMenu.Items) // this does not work!
{
HyperlinkButton hb = child as HyperlinkButton;
if (hb != null && hb.NavigateUri != null)
{
if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
{
VisualStateManager.GoToState(hb, "ActiveLink", true);
}
else
{
VisualStateManager.GoToState(hb, "InactiveLink", true);
}
}
}
The problem is that I can´t seem to find a way to enumerate the actual UI elements in the ItemsCollection.Items.
Anyone know how to do this or a possible workaround?
I can mention that what I´m trying to do is build a menu and submenu that display the hyperlinks clicked as a sort of breadcrumb.
UPDATE:
The best thing would be if I could get to that stackpanel somehow because this code seems to work:
foreach (UIElement child in LinksStackPanel.Children)
{
HyperlinkButton hb = child as HyperlinkButton;
if (hb != null && hb.NavigateUri != null)
{
if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
{
VisualStateManager.GoToState(hb, "ActiveLink", true);
}
else
{
VisualStateManager.GoToState(hb, "InactiveLink", true);
}
}
}
The solution looks like this:
foreach (var item in SubMenu.Items)
{
var hb = SubMenu.ItemContainerGenerator.ContainerFromItem(item).FindVisualChild<HyperlinkButton>();
if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
{
VisualStateManager.GoToState(hb, "ActiveLink", true);
}
else
{
VisualStateManager.GoToState(hb, "InactiveLink", true);
}
}
The extension method FindVisualChild:
public static T FindVisualChild<T>(this DependencyObject instance) where T : DependencyObject
{
T control = default(T);
if (instance != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(instance); i++)
{
if ((control = VisualTreeHelper.GetChild(instance, i) as T) != null)
{
break;
}
control = FindVisualChild<T>(VisualTreeHelper.GetChild(instance, i));
}
}
return control;
}
Try using the ItemContainerGenerator.ContainerFromItem method
foreach (var item in SubMenu.Items)
{
var child = SubMenu.ItemContainerGenerator.ContainerFromItem(item);
HyperlinkButton hb = child as HyperlinkButton;
// use hb
}
FindVisualChild from Johan Leino answer has bug: traversing of lower levels in control hierarchy does not have any effect because he don't check result of recursive call.
That is fixed version.
public static T FindVisualChild<T>(this DependencyObject instance) where T : DependencyObject
{
T control = default(T);
if (instance != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(instance); i++)
{
if ((control = VisualTreeHelper.GetChild(instance, i) as T) != null)
{
break;
}
if ((control = FindVisualChild<T>(VisualTreeHelper.GetChild(instance, i))) != null)
{
break;
}
}
}
return control;
}
Try this:
foreach (UIElement child in SubMenu.Items.OfType<UIElement>())
This is using the Enumerable.OfType<TResult> extension method that filters the collection down to only those items that are of the specified type.
I want to control DataGrid column visibility through a ContextMenu available to the user by right-clicking the column header. The ContextMenu displays the names of all available columns. I am using MVVM design pattern.
My question is: How do I bind the DataGridColumn's Visibility property to the IsChecked property of a MenuItem located in the ContextMenu.
Some mockup code:
<UserControl.Resources>
<ContextMenu x:Key="ColumnHeaderContextMenu">
<MenuItem Header="Menu Item..1" IsCheckable="True" />
</ContextMenu>
<Style x:Key="ColumnHeaderStyle"
TargetType="{x:Type toolkit:DataGridColumnHeader}">
<Setter Property="ContextMenu"
Value="{StaticResource ColumnHeaderContextMenu}" />
</Style>
<BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" />
</UserControl.Resources>
...flaf flaf flaf
<toolkit:DataGrid x:Name="MyGrid" AutoGenerateColumns="False"
ItemsSource="{Binding MyCollection, Mode=Default}"
EnableColumnVirtualization="True" IsReadOnly="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn Binding="{Binding Path=MyEntry}"
Header="MyEntry" Visibility="{Binding IsChecked, Converter=
{StaticResource booleanToVisibilityConverter}.... />
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
If I am being unclear please let me know and I will attempt to elaborate.
Cheers,
Updated 2021-09-29: Pasted the code from my old blog. I haven't worked with WPF in many years I know nothing about it anymore. This site has limits on post length so I had to share some of the example code using CodePile. See the links at bottom for usage. I believe the idea was the sample I made works in many scenarios versus just auto generated columns. I was working on a project where the columns were not known until runtime and could change dynamically. Also note there was a png file for the "eye" graphic, you'll need your own probably.
DataGridAPs.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
using Microsoft.Windows.Controls;
using Microsoft.Windows.Controls.Primitives;
namespace CanUserHideColumnDemo
{
public static class DataGridAPs
{
#region HideColumns
#region HideColumnsHeader
public static readonly DependencyProperty HideColumnsHeaderProperty =
DependencyProperty.RegisterAttached("HideColumnsHeader",
typeof(object), typeof(DataGridAPs));
public static object GetHideColumnsHeader(DataGrid obj)
{
return obj.GetValue(HideColumnsHeaderProperty);
}
public static void SetHideColumnsHeader(DataGrid obj, object value)
{
obj.SetValue(HideColumnsHeaderProperty, value);
}
#endregion HideColumnsHeader
#region HideColumnsHeaderTemplate
public static readonly DependencyProperty HideColumnsHeaderTemplateProperty =
DependencyProperty.RegisterAttached("HideColumnsHeaderTemplate",
typeof(DataTemplate), typeof(DataGridAPs));
public static DataTemplate GetHideColumnsHeaderTemplate(DataGrid obj)
{
return (DataTemplate)obj.GetValue(HideColumnsHeaderTemplateProperty);
}
public static void SetHideColumnsHeaderTemplate(DataGrid obj, DataTemplate value)
{
obj.SetValue(HideColumnsHeaderTemplateProperty, value);
}
#endregion HideColumnsHeaderTemplate
#region HideColumnsIcon
public static readonly DependencyProperty HideColumnsIconProperty =
DependencyProperty.RegisterAttached("HideColumnsIcon",
typeof(object), typeof(DataGridAPs));
public static object GetHideColumnsIcon(DataGrid obj)
{
return obj.GetValue(HideColumnsIconProperty);
}
public static void SetHideColumnsIcon(DataGrid obj, object value)
{
obj.SetValue(HideColumnsIconProperty, value);
}
#endregion HideColumnsIcon
#region CanUserHideColumns
public static readonly DependencyProperty CanUserHideColumnsProperty =
DependencyProperty.RegisterAttached("CanUserHideColumns",
typeof(bool), typeof(DataGridAPs),
new UIPropertyMetadata(false, OnCanUserHideColumnsChanged));
public static bool GetCanUserHideColumns(DataGrid obj)
{
return (bool)obj.GetValue(CanUserHideColumnsProperty);
}
public static void SetCanUserHideColumns(DataGrid obj, bool value)
{
obj.SetValue(CanUserHideColumnsProperty, value);
}
private static void OnCanUserHideColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = d as DataGrid;
if (dataGrid == null)
return;
if ((bool)e.NewValue == false)
{
dataGrid.Loaded -= new RoutedEventHandler(dataGrid_Loaded);
RemoveAllItems(dataGrid);
return;
}
if (!dataGrid.IsLoaded)
{
dataGrid.Loaded -= new RoutedEventHandler(dataGrid_Loaded);
dataGrid.Loaded += new RoutedEventHandler(dataGrid_Loaded);
}
else
SetupColumnHeaders(dataGrid);
}
private static void dataGrid_Loaded(object sender, RoutedEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
if (dataGrid == null)
return;
if (BindingOperations.IsDataBound(dataGrid, DataGrid.ItemsSourceProperty))
{
Binding b = BindingOperations.GetBinding(dataGrid, DataGrid.ItemsSourceProperty);
dataGrid.TargetUpdated += new EventHandler<DataTransferEventArgs>(dataGrid_TargetUpdated);
string xaml = XamlWriter.Save(b);
Binding b2 = XamlReader.Parse(xaml) as Binding;
if (b2 != null)
{
b2.NotifyOnTargetUpdated = true;
BindingOperations.ClearBinding(dataGrid, DataGrid.ItemsSourceProperty);
BindingOperations.SetBinding(dataGrid, DataGrid.ItemsSourceProperty, b2);
}
}
else
SetupColumnHeaders(dataGrid);
}
private static void dataGrid_TargetUpdated(object sender, DataTransferEventArgs e)
{
if (e.Property != DataGrid.ItemsSourceProperty)
return;
DataGrid dataGrid = sender as DataGrid;
if (dataGrid == null)
return;
EventHandler handler = null;
handler = delegate
{
RemoveAllItems(dataGrid);
if (SetupColumnHeaders(dataGrid))
dataGrid.LayoutUpdated -= handler;
};
dataGrid.LayoutUpdated += handler;
}
private static DataGridColumnHeader[] GetColumnHeaders(DataGrid dataGrid)
{
if (dataGrid == null)
return null;
dataGrid.UpdateLayout();
DataGridColumnHeader[] columnHeaders = CustomVisualTreeHelper<DataGridColumnHeader>.FindChildrenRecursive(dataGrid);
return (from DataGridColumnHeader columnHeader in columnHeaders
where columnHeader != null && columnHeader.Column != null
select columnHeader).ToArray();
}
private static string GetColumnName(DataGridColumn column)
{
if (column == null)
return string.Empty;
if (column.Header != null)
return column.Header.ToString();
else
return string.Format("Column {0}", column.DisplayIndex);
}
private static MenuItem GenerateItem(DataGrid dataGrid, DataGridColumn column)
{
if (column == null)
return null;
MenuItem item = new MenuItem();
item.Tag = column;
item.Header = GetColumnName(column);
if (string.IsNullOrEmpty(item.Header as string))
return null;
item.ToolTip = string.Format("Toggle column '{0}' visibility.", item.Header);
item.IsCheckable = true;
item.IsChecked = column.Visibility == Visibility.Visible;
item.Checked += delegate
{
SetItemIsChecked(dataGrid, column, true);
};
item.Unchecked += delegate
{
SetItemIsChecked(dataGrid, column, false);
};
return item;
}
public static MenuItem[] GetAttachedItems(DataGridColumnHeader columnHeader)
{
if (columnHeader == null || columnHeader.ContextMenu == null)
return null;
ItemsControl itemsContainer = (from object i in columnHeader.ContextMenu.Items
where i is MenuItem && ((MenuItem)i).Tag != null && ((MenuItem)i).Tag.ToString() == "ItemsContainer"
select i).FirstOrDefault() as MenuItem;
if (itemsContainer == null)
itemsContainer = columnHeader.ContextMenu;
return (from object i in itemsContainer.Items
where i is MenuItem && ((MenuItem)i).Tag is DataGridColumn
select i).Cast<MenuItem>().ToArray();
}
private static DataGridColumn GetColumnFromName(DataGrid dataGrid, string columnName)
{
if (string.IsNullOrEmpty(columnName))
return null;
foreach (DataGridColumn column in dataGrid.Columns)
{
if (GetColumnName(column) == columnName)
return column;
}
return null;
}
private static DataGridColumnHeader GetColumnHeaderFromColumn(DataGrid dataGrid, DataGridColumn column)
{
if (dataGrid == null || column == null)
return null;
DataGridColumnHeader[] columnHeaders = GetColumnHeaders(dataGrid);
return (from DataGridColumnHeader columnHeader in columnHeaders
where columnHeader.Column == column
select columnHeader).FirstOrDefault();
}
public static void RemoveAllItems(DataGrid dataGrid)
{
if (dataGrid == null)
return;
foreach (DataGridColumn column in dataGrid.Columns)
{
RemoveAllItems(dataGrid, column);
}
}
public static void RemoveAllItems(DataGrid dataGrid, DataGridColumn column)
{
if (dataGrid == null || column == null)
return;
DataGridColumnHeader columnHeader = GetColumnHeaderFromColumn(dataGrid, column);
List<MenuItem> itemsToRemove = new List<MenuItem>();
if (columnHeader == null)
return;
// Mark items and/or items container for removal.
if (columnHeader.ContextMenu != null)
{
foreach (object item in columnHeader.ContextMenu.Items)
{
if (item is MenuItem && ((MenuItem)item).Tag != null
&& (((MenuItem)item).Tag.ToString() == "ItemsContainer" || ((MenuItem)item).Tag is DataGridColumn))
itemsToRemove.Add((MenuItem)item);
}
}
// Remove items and/or items container.
foreach (MenuItem item in itemsToRemove)
{
columnHeader.ContextMenu.Items.Remove(item);
}
}
public static void ResetupColumnHeaders(DataGrid dataGrid)
{
RemoveAllItems(dataGrid);
SetupColumnHeaders(dataGrid);
}
private static void SetItemIsChecked(DataGrid dataGrid, DataGridColumn column, bool isChecked)
{
if (dataGrid == null || column == null)
return;
// Deny request if there are no other columns visible. Otherwise,
// they'd have no way of changing the visibility of any columns
// again.
//if (!isChecked && (from DataGridColumn c in dataGrid.Columns
// where c.Visibility == Visibility.Visible
// select c).Count() < 2)
// return;
if (isChecked && column.Visibility != Visibility.Visible)
{
ShowColumn(dataGrid, column);
}
else if (!isChecked)
column.Visibility = Visibility.Hidden;
DataGridColumnHeader[] columnHeaders = GetColumnHeaders(dataGrid);
ItemsControl itemsContainer = null;
object containerHeader = GetHideColumnsHeader(dataGrid);
foreach (DataGridColumnHeader columnHeader in columnHeaders)
{
itemsContainer = null;
if (columnHeader != null)
{
if (columnHeader.ContextMenu == null)
continue;
itemsContainer = (from object i in columnHeader.ContextMenu.Items
where i is MenuItem && ((MenuItem)i).Header == containerHeader
select i).FirstOrDefault() as MenuItem;
}
if (itemsContainer == null)
itemsContainer = columnHeader.ContextMenu;
foreach (object item in itemsContainer.Items)
{
if (item is MenuItem && ((MenuItem)item).Tag != null && ((MenuItem)item).Tag is DataGridColumn
&& ((MenuItem)item).Header.ToString() == GetColumnName(column))
{
((MenuItem)item).IsChecked = isChecked;
}
}
}
}
private static void SetupColumnHeader(DataGridColumnHeader columnHeader)
{
if (columnHeader == null)
return;
DataGrid dataGrid = CustomVisualTreeHelper<DataGrid>.FindAncestor(columnHeader);
if (dataGrid == null)
return;
DataGridColumnHeader[] columnHeaders = GetColumnHeaders(dataGrid);
if (columnHeaders == null)
return;
SetupColumnHeader(dataGrid, columnHeaders, columnHeader);
}
private static void SetupColumnHeader(DataGrid dataGrid, DataGridColumnHeader[] columnHeaders, DataGridColumnHeader columnHeader)
{
if (columnHeader.ContextMenu == null)
columnHeader.ContextMenu = new ContextMenu();
ItemsControl itemsContainer = null;
itemsContainer = columnHeader.ContextMenu;
object containerHeader = GetHideColumnsHeader(dataGrid);
if (containerHeader != null)
{
MenuItem ic = (from object i in columnHeader.ContextMenu.Items
where i is MenuItem && ((MenuItem)i).Tag != null && ((MenuItem)i).Tag.ToString() == "ItemsContainer"
select i).FirstOrDefault() as MenuItem;
if (ic == null)
{
itemsContainer = new MenuItem()
{
Header = containerHeader,
HeaderTemplate = GetHideColumnsHeaderTemplate(dataGrid) as DataTemplate,
Icon = GetHideColumnsIcon(dataGrid),
Tag = "ItemsContainer"
};
columnHeader.ContextMenu.Items.Add(itemsContainer);
}
else
return;
}
foreach (DataGridColumnHeader columnHeader2 in columnHeaders)
{
if (columnHeader2 != columnHeader
&& itemsContainer is ContextMenu
&& columnHeader2.ContextMenu == itemsContainer)
{
continue;
}
itemsContainer.Items.Add(GenerateItem(dataGrid, columnHeader2.Column));
}
}
public static bool SetupColumnHeaders(DataGrid dataGrid)
{
DataGridColumnHeader[] columnHeaders = GetColumnHeaders(dataGrid);
if (columnHeaders == null || columnHeaders.Count() == 0)
return false;
RemoveAllItems(dataGrid);
columnHeaders = GetColumnHeaders(dataGrid);
foreach (DataGridColumnHeader columnHeader in columnHeaders)
{
SetupColumnHeader(dataGrid, columnHeaders, columnHeader);
}
return true;
}
/// <summary>
/// Shows a column within the datagrid, which is not straightforward
/// because the datagrid not only hides a column when you tell it to
/// do so, but it also completely destroys its associated column
/// header. Meaning we need to set it up again. Before we can do
/// so we have to turn all columns back on again so we can get a
/// complete list of their column headers, then turn them back off
/// again.
/// </summary>
/// <param name="dataGrid"></param>
/// <param name="column"></param>
private static void ShowColumn(DataGrid dataGrid, DataGridColumn column)
{
if (dataGrid == null || column == null)
return;
column.Visibility = Visibility.Visible;
// Turn all columns on, but store their original visibility so we
// can restore it after we're done.
Dictionary<DataGridColumn, Visibility> vis = new Dictionary<DataGridColumn, Visibility>();
foreach (DataGridColumn c in dataGrid.Columns)
{
vis.Add(c, c.Visibility);
c.Visibility = Visibility.Visible;
}
dataGrid.UpdateLayout();
DataGridColumnHeader columnHeader = GetColumnHeaderFromColumn(dataGrid, column);
SetupColumnHeader(columnHeader);
foreach (DataGridColumn c in vis.Keys)
{
if ((Visibility)vis[c] != Visibility.Visible)
{
c.Visibility = (Visibility)vis[c];
}
}
dataGrid.UpdateLayout();
// Now we need to uncheck items that are associated with hidden
// columns.
SyncItemsOnColumnHeader(columnHeader);
}
private static void SyncItemsOnColumnHeader(DataGridColumnHeader columnHeader)
{
bool isVisible;
foreach (MenuItem item in GetAttachedItems(columnHeader))
{
if (item.Tag is DataGridColumn)
{
isVisible = ((DataGridColumn)item.Tag).Visibility == Visibility.Visible ? true : false;
if (item.IsChecked != isVisible)
{
item.IsChecked = isVisible;
}
}
}
}
#endregion CanUserHideColumns
#region CustomVisualTreeHelper
private static class CustomVisualTreeHelper<TReturn> where TReturn : DependencyObject
{
public static TReturn FindAncestor(DependencyObject descendant)
{
DependencyObject parent = descendant;
while (parent != null && !(parent is TReturn))
{
parent = VisualTreeHelper.GetParent(parent);
}
if (parent != null)
{
return (TReturn)parent;
}
return default(TReturn);
}
public static TReturn FindChild(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
DependencyObject child = null;
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
child = VisualTreeHelper.GetChild(parent, childIndex);
if (child is TReturn)
{
return (TReturn)(object)child;
}
}
return default(TReturn);
}
public static TReturn FindChildRecursive(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
DependencyObject child = null;
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
child = VisualTreeHelper.GetChild(parent, childIndex);
if (child is TReturn)
{
return (TReturn)(object)child;
}
else
{
child = CustomVisualTreeHelper<TReturn>.FindChildRecursive(child);
if (child is TReturn)
{
return (TReturn)(object)child;
}
}
}
return default(TReturn);
}
public static TReturn[] FindChildren(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
DependencyObject child = null;
List<TReturn> children = new List<TReturn>(childCount);
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
child = VisualTreeHelper.GetChild(parent, childIndex);
if (child is TReturn)
{
children[childIndex] = (TReturn)(object)child;
}
}
return children.ToArray();
}
public static TReturn[] FindChildrenRecursive(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
DependencyObject child = null;
List<TReturn> children = new List<TReturn>();
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
child = VisualTreeHelper.GetChild(parent, childIndex);
if (child is TReturn)
{
children.Add((TReturn)(object)child);
}
children.AddRange(CustomVisualTreeHelper<TReturn>.FindChildrenRecursive(child));
}
return children.ToArray();
}
}
#endregion CustomVisualTreeHelper
#endregion HideColumns
}
}
Window1.xaml
Window1.xaml.cs
I've been looking for a generic, XAML (i.e., no code-behind), automatic and simple example of a column chooser context menu that binds to a WPF DataGrid column header. I've read literally hundreds of articles, but none of them seem to do exactly the right thing, or they're no generic enough. So here's what I think is the best combined solution:
First, put these in the resource dictionary. I'll leave it as an exercise to the reader to write the Visibility/Boolean converter to ensure the checkboxes check when the column is visible and vice-versa. Note that by defining x:Shared="False" for the context menu resource, it'll get instance-specific state which means that you can use this single template/resource for all your datagrids and they'll all maintain their own state.
<Converters:VisiblityToInverseBooleanConverter x:Key="VisiblityToInverseBooleanConverter"/>
<ContextMenu x:Key="ColumnChooserMenu" x:Shared="False"
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
ItemsSource="{Binding Columns, RelativeSource={RelativeSource AncestorType={x:Type sdk:DataGrid}}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="AutomationProperties.Name" Value="{Binding Header}"/>
<Setter Property="IsCheckable" Value="True" />
<Setter Property="IsChecked" Value="{Binding Visibility, Mode=TwoWay, Converter={StaticResource VisiblityToInverseBooleanConverter}}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type Primitives:DataGridColumnHeader}">
<Setter Property="ContextMenu" Value="{StaticResource ColumnChooserMenu}" />
</Style>
<ContextMenu x:Key="GridItemsContextMenu" >
<MenuItem Header="Launch Do Some other action"/>
</ContextMenu>
Then define the DataGrid as follows (where OrdersQuery is some data source exposed by the View-model):
<sdk:DataGrid ItemsSource="{Binding OrdersQuery}"
AutoGenerateColumns="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"
ContextMenu="{StaticResource GridItemsContextMenu}">
<!-- rest of datagrid stuff goes here -->
</sdk:DataGrid>
This will give you the following:
A context menu bound to the column headings which acts as a column chooser.
A context menu bound to the items in the grid (to perform actions on the items themselves - again, the binding of the actions is an exercise for the reader).
Hope this helps people who've been looking for an example like this.
I know this is a bit old. But I was looking at doing this and this post is much simpler:
http://iimaginec.wordpress.com/2011/07/25/binding-wpf-toolkit%E2%80%99s-datagridcolumn-to-a-viewmodel-datacontext-propogation-for-datagrid-columns-the-mvvm-way-to-interact-with-datagridcolumn/
All you need to do is set DataContext on the Columns and then bind Visibility to your ViewModel as per normal!
:) Simple and effective
Ok, this has been quite the exercise for a WPF n00b.
IanR thanks for the suggestion I used a similar aproach but it dosent take you all the way.
Here is what I have come up with if anyone can find a more consistent way of doing it I will appreciate any comments:
Impediments:
DataGridColumnHeader does not support a context menu. Therefore the context menu needs to be applied as a Style.
The contextmenu has its own datacontext so we have to use findancestor to link it to the ViewModels datacontext.
ATM the DataGrid control does not parse its datacontext to its Columns. This could be solved in codebehind however we are using the MVVM pattern so I decided to follow jamiers approach
Solution:
Place the following two blocks of code in Window.Resources
<Style x:Key="ColumnHeaderStyle"
TargetType="{x:Type toolkit:DataGridColumnHeader}">
<Setter Property="ContextMenu"
Value="{StaticResource ColumnHeaderContextMenu}" />
</Style>
<ContextMenu x:Key="ColumnHeaderContextMenu">
<MenuItem x:Name="MyMenuItem"
IsCheckable="True"
IsChecked="{Binding DataContext.IsHidden, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type toolkit:DataGrid}}}"/>
</ContextMenu>
The datagrid then looks something like this in XAML
<toolkit:DataGrid x:Name="MyGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding SampleCollection, Mode=Default}"
EnableColumnVirtualization="True"
IsReadOnly="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn Binding="{Binding Path=SamplingUser}"
Header="{Binding (FrameworkElement.DataContext).IsHidden, RelativeSource={x:Static RelativeSource.Self}}"
Visibility="{Binding (FrameworkElement.DataContext).IsHidden,
RelativeSource={x:Static RelativeSource.Self},
Converter={StaticResource booleanToVisibilityConverter}}"/>
So the visibility property on the DataGridColumn and the ischeked property are both databound to the IsHidden property on the viewModel.
In the ViewModel:
public bool IsHidden
{
get { return isHidden; }
set
{ if (value != isHidden)
{
isHidden = value;
OnPropertyChanged("IsHidden");
OnPropertyChanged("IsVisible");
}
}
}
The Helper class defined by Jaimer:
class DataGridSupport
{
static DataGridSupport()
{
DependencyProperty dp = FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn));
FrameworkElement.DataContextProperty.OverrideMetadata ( typeof(DataGrid), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnDataContextChanged)));
}
public static void OnDataContextChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid grid = d as DataGrid ;
if ( grid != null )
{
foreach ( DataGridColumn col in grid.Columns )
{
col.SetValue ( FrameworkElement.DataContextProperty, e.NewValue );
}
}
}
}
Instanciated in the viewmodel (just to show done through Unity in real project)
private static DataGridSupport dc = new DataGridSupport();
Cheers,
Instead of booleanToVisibilityConverter you can use x:static
<Setter TargetName="UIElement" Property="UIElement.Visibility" Value="x:Static Visibility.Hidden" />
Statics in XAML:
http://msdn.microsoft.com/en-us/library/ms742135.aspx
I did try to ge this to bind to the ContextMenu using 'ElementName', but in the end, got it work using Properties in the VM, e.g.
bool _isHidden;
public bool IsHidden
{
get { return _isHidden; }
set
{
if (value != _isHidden)
{
_isHidden = value;
RaisePropertyChanged("IsHidden");
RaisePropertyChanged("IsVisible");
}
}
}
public Visibility IsVisible
{
get { return IsHidden ? Visibility.Hidden : Visibility.Visible; }
}
and in the XAML:
<Window.ContextMenu>
<ContextMenu>
<MenuItem Header="Hidden" IsCheckable="True" IsChecked="{Binding IsHidden}" />
</ContextMenu>
</Window.ContextMenu>
<toolkit:DataGrid x:Name="MyGrid" AutoGenerateColumns="False" ItemsSource="{Binding MyCollection, Mode=Default}" EnableColumnVirtualization="True" IsReadOnly="True" ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn Binding="{Binding Path=MyEntry}" Header="MyEntry" Visibility="{Binding Path=IsVisible, Mode=OneWay}" />
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>