以编程方式生成的扩展器,可滚动内容的大小调整为 window
Programmatically generated expanders with scrollable content resizing to window
我正在寻找一种更好的方法来使用 following behavior.
以编程方式生成 window
即:可以以编程方式初始化的扩展器列表,每个扩展器包含的可滚动内容大于可以在 window 中显示的内容(在本例中为数据网格)。当扩展器被扩展时,它的内容被限制在 window 的可用大小内,同时允许查看和操作所有其他扩展器。此外,在任何给定时间只能打开一个扩展器。
这个功能似乎在很多应用程序菜单中都有用,所以我很惊讶它实现起来有多么困难。有没有比我做的更好的方法?
XAML(出奇的简单):
<Window x:Class="ExpanderScrollExample.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="200">
<Grid>
<ItemsControl ItemsSource="{Binding Dict}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Loaded="GridLoaded" ScrollViewer.CanContentScroll="False"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header ="{Binding Key, Mode=OneWay}" Expanded="Expander_Expanded" Collapsed="Expander_Collapsed">
<DataGrid ItemsSource="{Binding Path=Value}"/>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
代码隐藏(大部分魔法):
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ExpanderScrollExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
private void GridLoaded(object sender, RoutedEventArgs e)
{
Grid grid = sender as Grid;
grid.LayoutUpdated += (s, e2) =>
{
var childCount = grid.Children.Count;
int rowsToAdd = (childCount - grid.RowDefinitions.Count);
for (int row = 0; row < rowsToAdd; row++)
{
RowDefinition rowDefinition = new RowDefinition();
rowDefinition.Height = new GridLength(0, GridUnitType.Auto);
grid.RowDefinitions.Add(rowDefinition);
}
for (int i = 0; i < childCount; i++)
{
var child = grid.Children[i] as FrameworkElement;
Grid.SetRow(child, i);
}
};
}
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
ContentPresenter parentDataContext = VisualTreeHelper.GetParent(sender as DependencyObject) as ContentPresenter;
Grid grid = VisualTreeHelper.GetParent(parentDataContext as DependencyObject) as Grid;
grid.RowDefinitions[Grid.GetRow(parentDataContext)].Height = new GridLength(1, GridUnitType.Star);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(grid); i++)
{
DependencyObject neighborDataContext = VisualTreeHelper.GetChild(grid, i);
for (int j = 0; j < VisualTreeHelper.GetChildrenCount(neighborDataContext); j++)
{
DependencyObject neighborExpander = VisualTreeHelper.GetChild(neighborDataContext, j);
if (neighborExpander is Expander && neighborExpander != sender)
{
((Expander)neighborExpander).IsExpanded = false;
this.Collapse(neighborExpander as Expander);
}
}
}
}
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
this.Collapse(sender as Expander);
}
private void Collapse(Expander expander)
{
ContentPresenter parent = VisualTreeHelper.GetParent(expander as DependencyObject) as ContentPresenter;
Grid grandparent = VisualTreeHelper.GetParent(parent as DependencyObject) as Grid;
grandparent.RowDefinitions[Grid.GetRow(parent)].Height = new GridLength(0, GridUnitType.Auto);
}
}
}
ViewModel(仅用于数据生成)
using System.Collections.Generic;
namespace ExpanderScrollExample
{
class MainWindowViewModel
{
public Dictionary<string, List<MyClass>> Dict { get; }
public MainWindowViewModel()
{
Dict = new Dictionary<string, List<MyClass>>();
for ( int i = 0; i < 5; i++ )
{
string key = "Header " + i.ToString();
Dict[key] = new List<MyClass>();
for ( int j = 0; j < 100; j++)
{
Dict[key].Add(new MyClass(j, i*100 + j));
}
}
}
public class MyClass
{
public int Column1 {get; set;}
public int Column2 { get; set; }
public MyClass( int column1, int column2)
{
Column1 = column1;
Column2 = column2;
}
}
}
}
我通常也尝试使用 MVVM 模式进行确认,但在这种情况下无法这样做。
在网格中生成项目控件取自 here.
我还考虑过将样式触发器用于 expand/collapse 并按照 中所述设置大小,但我想不出如何使用扩展器绑定动态生成的行的好方法。
有更好的方法吗?
1.在给定时间只扩展了一个 Expander
要确保在给定时间只展开一个 Expander
,您可以创建一个 Attached Behavior
。我实现了一个供参考。
ExpanderGroupBehavior(灵感来自 RadioButton
)
<Expander wpf:ExpanderGroupBehavior.GroupName="ExpanderGroup01" />
这确保同一组中只有一个 Expander
被展开。
2。扩展 Expander
填充可用 space
为此,您可以创建自己的 Panel
来为您处理。
参见 How to get controls in WPF to fill available space? and https://docs.microsoft.com/en-us/dotnet/framework/wpf/controls/how-to-create-a-custom-panel-element
我正在寻找一种更好的方法来使用 following behavior.
以编程方式生成 window即:可以以编程方式初始化的扩展器列表,每个扩展器包含的可滚动内容大于可以在 window 中显示的内容(在本例中为数据网格)。当扩展器被扩展时,它的内容被限制在 window 的可用大小内,同时允许查看和操作所有其他扩展器。此外,在任何给定时间只能打开一个扩展器。
这个功能似乎在很多应用程序菜单中都有用,所以我很惊讶它实现起来有多么困难。有没有比我做的更好的方法?
XAML(出奇的简单):
<Window x:Class="ExpanderScrollExample.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="200">
<Grid>
<ItemsControl ItemsSource="{Binding Dict}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Loaded="GridLoaded" ScrollViewer.CanContentScroll="False"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header ="{Binding Key, Mode=OneWay}" Expanded="Expander_Expanded" Collapsed="Expander_Collapsed">
<DataGrid ItemsSource="{Binding Path=Value}"/>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
代码隐藏(大部分魔法):
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ExpanderScrollExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
private void GridLoaded(object sender, RoutedEventArgs e)
{
Grid grid = sender as Grid;
grid.LayoutUpdated += (s, e2) =>
{
var childCount = grid.Children.Count;
int rowsToAdd = (childCount - grid.RowDefinitions.Count);
for (int row = 0; row < rowsToAdd; row++)
{
RowDefinition rowDefinition = new RowDefinition();
rowDefinition.Height = new GridLength(0, GridUnitType.Auto);
grid.RowDefinitions.Add(rowDefinition);
}
for (int i = 0; i < childCount; i++)
{
var child = grid.Children[i] as FrameworkElement;
Grid.SetRow(child, i);
}
};
}
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
ContentPresenter parentDataContext = VisualTreeHelper.GetParent(sender as DependencyObject) as ContentPresenter;
Grid grid = VisualTreeHelper.GetParent(parentDataContext as DependencyObject) as Grid;
grid.RowDefinitions[Grid.GetRow(parentDataContext)].Height = new GridLength(1, GridUnitType.Star);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(grid); i++)
{
DependencyObject neighborDataContext = VisualTreeHelper.GetChild(grid, i);
for (int j = 0; j < VisualTreeHelper.GetChildrenCount(neighborDataContext); j++)
{
DependencyObject neighborExpander = VisualTreeHelper.GetChild(neighborDataContext, j);
if (neighborExpander is Expander && neighborExpander != sender)
{
((Expander)neighborExpander).IsExpanded = false;
this.Collapse(neighborExpander as Expander);
}
}
}
}
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
this.Collapse(sender as Expander);
}
private void Collapse(Expander expander)
{
ContentPresenter parent = VisualTreeHelper.GetParent(expander as DependencyObject) as ContentPresenter;
Grid grandparent = VisualTreeHelper.GetParent(parent as DependencyObject) as Grid;
grandparent.RowDefinitions[Grid.GetRow(parent)].Height = new GridLength(0, GridUnitType.Auto);
}
}
}
ViewModel(仅用于数据生成)
using System.Collections.Generic;
namespace ExpanderScrollExample
{
class MainWindowViewModel
{
public Dictionary<string, List<MyClass>> Dict { get; }
public MainWindowViewModel()
{
Dict = new Dictionary<string, List<MyClass>>();
for ( int i = 0; i < 5; i++ )
{
string key = "Header " + i.ToString();
Dict[key] = new List<MyClass>();
for ( int j = 0; j < 100; j++)
{
Dict[key].Add(new MyClass(j, i*100 + j));
}
}
}
public class MyClass
{
public int Column1 {get; set;}
public int Column2 { get; set; }
public MyClass( int column1, int column2)
{
Column1 = column1;
Column2 = column2;
}
}
}
}
我通常也尝试使用 MVVM 模式进行确认,但在这种情况下无法这样做。
在网格中生成项目控件取自 here.
我还考虑过将样式触发器用于 expand/collapse 并按照
有更好的方法吗?
1.在给定时间只扩展了一个 Expander
要确保在给定时间只展开一个 Expander
,您可以创建一个 Attached Behavior
。我实现了一个供参考。
ExpanderGroupBehavior(灵感来自 RadioButton
)
<Expander wpf:ExpanderGroupBehavior.GroupName="ExpanderGroup01" />
这确保同一组中只有一个 Expander
被展开。
2。扩展 Expander
填充可用 space
为此,您可以创建自己的 Panel
来为您处理。
参见 How to get controls in WPF to fill available space? and https://docs.microsoft.com/en-us/dotnet/framework/wpf/controls/how-to-create-a-custom-panel-element