动态添加视图并在网格中排列
Adding views dynamically and arranging in grid
我正在使用 WPF 构建桌面应用程序,我需要能够以一种看起来井井有条的方式动态添加视图。
我创建了一个 ObservableCollection
,并将 itemsControl.ItemsSource
设置为该集合。
我可以添加视图,它看起来很糟糕。
关于可以添加的观看次数,我只知道最多可以有 16 个观看次数。
我想创建一个动态网格,它会根据可用的视图数量而变化,如下图所示
所以我尝试采用动态网格的想法,
每次视图 added/removed(听起来很糟糕)时,我都会创建一个新网格,根据视图集合的大小,我知道如何正确拆分网格。
然后我遍历集合中的所有现有视图,并按照正确的逻辑将它们放置在网格上。我在这里搞得一团糟,所以如果我的方向完全错误,请告诉我。
代码如下:
public partial class DynamicTargetView : UserControl
{
private ObservableCollection<TargetView> views = new ObservableCollection<TargetView>();
public Grid grid;
public DynamicTargetView()
{
InitializeComponent();
SettingsBar.onViewChange += addOrRemoveTargetsFromPanel;
}
public void addOrRemoveTargetsFromPanel(Object sender, WeiTargetGui.EventArgs.AddOrRemoveViewEventArgs e)
{
if (e.isShown)
{
addTargetToPanel(e.id);
}
else removeTargetFromPanel(e.id);
}
public void addTargetToPanel(string id)
{
views.Add(new TargetView(Int32.Parse(id)));
ArrangeGrid();
}
public void removeTargetFromPanel(string id)
{
foreach (TargetView v in views)
{
if (v.id == Int32.Parse(id))
views.Remove(v);
}
ArrangeGrid();
}
public void ArrangeGrid()
{
int NumOfViews = views.Count();
grid = new Grid();
grid.Children.Clear();
ColumnDefinition gridCol1;
ColumnDefinition gridCol2;
ColumnDefinition gridCol3;
ColumnDefinition gridCol4;
RowDefinition gridRow1;
RowDefinition gridRow2;
RowDefinition gridRow3;
RowDefinition gridRow4;
if (NumOfViews == 1)
{
addtoGrid(0, 0);
}
else if (NumOfViews == 2)
{
gridCol1 = new ColumnDefinition();
gridCol2 = new ColumnDefinition();
grid.ColumnDefinitions.Add(gridCol1);
grid.ColumnDefinitions.Add(gridCol2);
addtoGrid(2, 0);
}
else if (NumOfViews < 5)
{
gridCol1 = new ColumnDefinition();
gridCol2 = new ColumnDefinition();
gridRow1 = new RowDefinition();
gridRow2 = new RowDefinition();
grid.ColumnDefinitions.Add(gridCol1);
grid.ColumnDefinitions.Add(gridCol2);
grid.RowDefinitions.Add(gridRow1);
grid.RowDefinitions.Add(gridRow2);
addtoGrid(2, 2);
}
else if (NumOfViews < 10)
{
gridCol1 = new ColumnDefinition();
gridCol2 = new ColumnDefinition();
gridCol3 = new ColumnDefinition();
gridRow1 = new RowDefinition();
gridRow2 = new RowDefinition();
gridRow3 = new RowDefinition();
grid.ColumnDefinitions.Add(gridCol1);
grid.ColumnDefinitions.Add(gridCol2);
grid.ColumnDefinitions.Add(gridCol3);
grid.RowDefinitions.Add(gridRow1);
grid.RowDefinitions.Add(gridRow2);
grid.RowDefinitions.Add(gridRow3);
addtoGrid(3, 3);
}
else if (NumOfViews < 17)
{
gridCol1 = new ColumnDefinition();
gridCol2 = new ColumnDefinition();
gridCol3 = new ColumnDefinition();
gridCol4 = new ColumnDefinition();
gridRow1 = new RowDefinition();
gridRow2 = new RowDefinition();
gridRow3 = new RowDefinition();
gridRow4 = new RowDefinition();
grid.ColumnDefinitions.Add(gridCol1);
grid.ColumnDefinitions.Add(gridCol2);
grid.ColumnDefinitions.Add(gridCol3);
grid.ColumnDefinitions.Add(gridCol4);
grid.RowDefinitions.Add(gridRow1);
grid.RowDefinitions.Add(gridRow2);
grid.RowDefinitions.Add(gridRow3);
grid.RowDefinitions.Add(gridRow4);
addtoGrid(4, 4);
}
}
public void addtoGrid(int cols, int rows)
{
int row = 0;
int column = 0;
foreach (var view in views)
{
if (cols == 0 && rows == 0)
{
grid.Children.Add(view);
break;
}
if (cols == 2 && rows == 0)
{
Grid.SetColumn(view, column);
column++;
grid.Children.Add(view);
}
else
{
if (column != cols)
{
Grid.SetColumn(view, column);
Grid.SetRow(view, row);
grid.Children.Add(view);
}
if (column < cols)
column++;
else
{
column = 0;
row++;
}
}
}
this.Content = grid;
}
}
TargetView
是一个用户控件,它表示一个目标以及有关该目标的一些数据。
所以这是代码的问题 -
1) 当向视图集合中添加多个组件时,出现以下异常:
System.InvalidOperationException: 'Specified element is already the logical child of another element. Disconnect it first.'
我添加了 grid.Children.Clear()
- 没有任何帮助。
2) 最后一个问题 - 代码看起来很糟糕,我只是想让它工作,以便下次我可以更好地学习和实施它。
将不胜感激,非常感谢。
你的做法完全错了。如果您正确地做事并使用数据绑定,即使不是成熟的 MVVM,您也会为自己省去很多麻烦。
每当您想在 WPF 中显示 "things"(视图或其他任何内容)的列表时,您应该使用绑定到项目集合的 ItemsSource,其中包含您要创建的视图的逻辑。执行此操作时,您会看到看起来像常规列表框的内容,每个元素都是包含 class 名称的一行文本。诀窍是然后使用 DataTemplates 指定为每种后端类型创建的视图类型,即:
<DataTemplate TargetType="{x:Type local:MyViewModelTypeA}">
<view:MyViewTypeA />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:MyViewModelTypeB}">
<view:MyViewTypeB />
</DataTemplate>
执行此操作,您的视图现在看起来是正确的,但它们仍将位于垂直 StackPanel 中。要更改它,您可以替换默认的 ItemsPanel:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
如果您想要更多 "grid-like",那么您也可以使用 UniformGrid,并将其 NumColumns 绑定到父 class 中的 属性,它会在运行时计算该值.
这个有很多变体和许多不同的参数需要调整,但这是一般的想法。 WPF 的关键是在您的 DataContext classes 中执行尽可能多的逻辑,并使用尽可能轻量级的绑定将您的前端绑定到它。
如果元素大小相同,则使用 UniformGrid 而不是 Grid,这样可以避免不必要的计算
我正在使用 WPF 构建桌面应用程序,我需要能够以一种看起来井井有条的方式动态添加视图。
我创建了一个 ObservableCollection
,并将 itemsControl.ItemsSource
设置为该集合。
我可以添加视图,它看起来很糟糕。
关于可以添加的观看次数,我只知道最多可以有 16 个观看次数。
我想创建一个动态网格,它会根据可用的视图数量而变化,如下图所示
所以我尝试采用动态网格的想法, 每次视图 added/removed(听起来很糟糕)时,我都会创建一个新网格,根据视图集合的大小,我知道如何正确拆分网格。
然后我遍历集合中的所有现有视图,并按照正确的逻辑将它们放置在网格上。我在这里搞得一团糟,所以如果我的方向完全错误,请告诉我。
代码如下:
public partial class DynamicTargetView : UserControl
{
private ObservableCollection<TargetView> views = new ObservableCollection<TargetView>();
public Grid grid;
public DynamicTargetView()
{
InitializeComponent();
SettingsBar.onViewChange += addOrRemoveTargetsFromPanel;
}
public void addOrRemoveTargetsFromPanel(Object sender, WeiTargetGui.EventArgs.AddOrRemoveViewEventArgs e)
{
if (e.isShown)
{
addTargetToPanel(e.id);
}
else removeTargetFromPanel(e.id);
}
public void addTargetToPanel(string id)
{
views.Add(new TargetView(Int32.Parse(id)));
ArrangeGrid();
}
public void removeTargetFromPanel(string id)
{
foreach (TargetView v in views)
{
if (v.id == Int32.Parse(id))
views.Remove(v);
}
ArrangeGrid();
}
public void ArrangeGrid()
{
int NumOfViews = views.Count();
grid = new Grid();
grid.Children.Clear();
ColumnDefinition gridCol1;
ColumnDefinition gridCol2;
ColumnDefinition gridCol3;
ColumnDefinition gridCol4;
RowDefinition gridRow1;
RowDefinition gridRow2;
RowDefinition gridRow3;
RowDefinition gridRow4;
if (NumOfViews == 1)
{
addtoGrid(0, 0);
}
else if (NumOfViews == 2)
{
gridCol1 = new ColumnDefinition();
gridCol2 = new ColumnDefinition();
grid.ColumnDefinitions.Add(gridCol1);
grid.ColumnDefinitions.Add(gridCol2);
addtoGrid(2, 0);
}
else if (NumOfViews < 5)
{
gridCol1 = new ColumnDefinition();
gridCol2 = new ColumnDefinition();
gridRow1 = new RowDefinition();
gridRow2 = new RowDefinition();
grid.ColumnDefinitions.Add(gridCol1);
grid.ColumnDefinitions.Add(gridCol2);
grid.RowDefinitions.Add(gridRow1);
grid.RowDefinitions.Add(gridRow2);
addtoGrid(2, 2);
}
else if (NumOfViews < 10)
{
gridCol1 = new ColumnDefinition();
gridCol2 = new ColumnDefinition();
gridCol3 = new ColumnDefinition();
gridRow1 = new RowDefinition();
gridRow2 = new RowDefinition();
gridRow3 = new RowDefinition();
grid.ColumnDefinitions.Add(gridCol1);
grid.ColumnDefinitions.Add(gridCol2);
grid.ColumnDefinitions.Add(gridCol3);
grid.RowDefinitions.Add(gridRow1);
grid.RowDefinitions.Add(gridRow2);
grid.RowDefinitions.Add(gridRow3);
addtoGrid(3, 3);
}
else if (NumOfViews < 17)
{
gridCol1 = new ColumnDefinition();
gridCol2 = new ColumnDefinition();
gridCol3 = new ColumnDefinition();
gridCol4 = new ColumnDefinition();
gridRow1 = new RowDefinition();
gridRow2 = new RowDefinition();
gridRow3 = new RowDefinition();
gridRow4 = new RowDefinition();
grid.ColumnDefinitions.Add(gridCol1);
grid.ColumnDefinitions.Add(gridCol2);
grid.ColumnDefinitions.Add(gridCol3);
grid.ColumnDefinitions.Add(gridCol4);
grid.RowDefinitions.Add(gridRow1);
grid.RowDefinitions.Add(gridRow2);
grid.RowDefinitions.Add(gridRow3);
grid.RowDefinitions.Add(gridRow4);
addtoGrid(4, 4);
}
}
public void addtoGrid(int cols, int rows)
{
int row = 0;
int column = 0;
foreach (var view in views)
{
if (cols == 0 && rows == 0)
{
grid.Children.Add(view);
break;
}
if (cols == 2 && rows == 0)
{
Grid.SetColumn(view, column);
column++;
grid.Children.Add(view);
}
else
{
if (column != cols)
{
Grid.SetColumn(view, column);
Grid.SetRow(view, row);
grid.Children.Add(view);
}
if (column < cols)
column++;
else
{
column = 0;
row++;
}
}
}
this.Content = grid;
}
}
TargetView
是一个用户控件,它表示一个目标以及有关该目标的一些数据。
所以这是代码的问题 -
1) 当向视图集合中添加多个组件时,出现以下异常:
System.InvalidOperationException: 'Specified element is already the logical child of another element. Disconnect it first.'
我添加了 grid.Children.Clear()
- 没有任何帮助。
2) 最后一个问题 - 代码看起来很糟糕,我只是想让它工作,以便下次我可以更好地学习和实施它。
将不胜感激,非常感谢。
你的做法完全错了。如果您正确地做事并使用数据绑定,即使不是成熟的 MVVM,您也会为自己省去很多麻烦。
每当您想在 WPF 中显示 "things"(视图或其他任何内容)的列表时,您应该使用绑定到项目集合的 ItemsSource,其中包含您要创建的视图的逻辑。执行此操作时,您会看到看起来像常规列表框的内容,每个元素都是包含 class 名称的一行文本。诀窍是然后使用 DataTemplates 指定为每种后端类型创建的视图类型,即:
<DataTemplate TargetType="{x:Type local:MyViewModelTypeA}">
<view:MyViewTypeA />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:MyViewModelTypeB}">
<view:MyViewTypeB />
</DataTemplate>
执行此操作,您的视图现在看起来是正确的,但它们仍将位于垂直 StackPanel 中。要更改它,您可以替换默认的 ItemsPanel:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
如果您想要更多 "grid-like",那么您也可以使用 UniformGrid,并将其 NumColumns 绑定到父 class 中的 属性,它会在运行时计算该值.
这个有很多变体和许多不同的参数需要调整,但这是一般的想法。 WPF 的关键是在您的 DataContext classes 中执行尽可能多的逻辑,并使用尽可能轻量级的绑定将您的前端绑定到它。
如果元素大小相同,则使用 UniformGrid 而不是 Grid,这样可以避免不必要的计算