如何制作相似外观对象的网格?
How to make a grid of similar looking objects?
我正准备编写一些桌面应用程序来模拟一些东西,我认为这是尝试一些新技术的好机会。由于应用程序适用于 Windows,并且我在某处看到 Visual Studio 社区版,因此我决定尝试 WPF。
事情是这样的。基本视图应该看起来像一个简单的网格,其中每个矩形都是一个实际的文本框。我可以在每一个中点击并写一些文字。
实现这一目标还算不错。起初我玩的是 Grid 和 ColumnDefinitions 以及 RowDefinitions,这对硬编码的那些很有用。之后,我尝试了 ItemsControl 和 ItemTemplate,就差不多了。
但是现在剧情出现了转折。我希望能够编辑每一个 TextBox。通过编辑,我的意思是将它分成几个较小的文本框。因此,如果我将第 2 个 1 分成 2 块,第 3 个 1 分成 3 块,它应该看起来像:
而且我不知道如何解决这个问题。由于它与其他的不一样,我认为我不能再将 ItemsControl 与模板一起使用(或者我可以吗?)。我是 WPF 的新手,所以也许有一些我还没有看到的明显的东西。因此,如果有人非常了解 WPF 并且可以指出正确的方向或者至少告诉我 "What are you doing? WPF is not good for that kind of apps, use XXX instead".
您可以使用 ÌtemTemplateSelector
根据变量分配不同的模板。
在您后面的代码中,我们有一个简单的 Rows
属性。这将用于显示网格中显示的行数。
MainWindow.cs
public partial class MainWindow : Window
{
public int[] Rows{ get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Rows= new int[] { 1, 2, 1, 3 };
}
}
根据Rows
选出正确的DataTemplate
的ItemTemplateSelector.cs
public class RowTemplateSelecter: DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
int rows = 1;
int.TryParse(item.ToString(), out rows);
switch (rows)
{
case 1:
return element.FindResource("OneRow") as DataTemplate;
case 2:
return element.FindResource("TwoRows") as DataTemplate;
case 3:
return element.FindResource("ThreeRows") as DataTemplate;
default:
return element.FindResource("OneRow") as DataTemplate;
}
}
}
最后 MainWindow.xaml 添加 3 个模板和 ItemTemplateSelector
<Window.Resources>
<DataTemplate x:Key="OneRow">
<Grid Background="Red" Width="100" Height="100">
</Grid>
</DataTemplate>
<DataTemplate x:Key="TwoRows" >
<Grid Width="100" Height="100">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Fill="Blue"/>
<Rectangle Fill="Green" Grid.Row="1"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="ThreeRows">
<Grid Width="100" Height="100">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Fill="Yellow"/>
<Rectangle Fill="Orange" Grid.Row="1"/>
<Rectangle Fill="Black" Grid.Row="2"/>
</Grid>
</DataTemplate>
<local:RowTemplateSelecter x:Key="RowSelector"/>
</Window.Resources>
<Grid>
<ItemsControl x:Name="rectangles"
ItemsSource="{Binding Rows}"
ItemTemplateSelector="{StaticResource RowSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
希望对您有所帮助
问得好,根据您的应用程序的功能,以及如何将每个控件的数据连接到您的 app/model/whatever。
,当然有很多布局控件的方法
此答案侧重于布局,同时占用所有可用 space 并允许复制每个容器中的内容。内容很好地流动,直到 window 变得太小、太窄等,这就是您需要决定您的应用程序将允许用户做什么的地方。在这段代码达到生产质量之前还有很多工作要做,但它是一些 WPF 基础知识的一个很好的例子,可以帮助您熟悉 WPF 平台。
我添加了一些边框、边距和背景颜色用于测试,这样您就可以看到哪个容器占据了哪个 space。适合测试;可能会在最终版本中删除或更改为透明。
主窗口
XAML
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Text="WPF" FontSize="36" Margin="20" Foreground="Orange" HorizontalAlignment="Center"/>
</StackPanel>
<Grid Grid.Row="1" Margin="5">
<Border Background="LightGray" BorderBrush="Red" BorderThickness="1">
<UniformGrid Columns="4" Name="MainPanel"/>
</Border>
</Grid>
</Grid>
代码
public partial class MainWindow : Window
{
private static int _nextId = 0;
public static int NextId
{
get { return _nextId++; }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// add non-multiple of 8 to see how layout works
for (var i=0; i<7; i++)
{
MainPanel.Children.Add(new EditPanelControl());
}
}
}
EditPanelControl(用户控件)
XAML
<Grid Margin="5">
<Border Background="LightYellow" BorderBrush="Green" BorderThickness="1">
<!-- Make this a viewbox if you want to show all items but have them shrink -->
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Name="MainPanel" VerticalAlignment="Center"/>
</ScrollViewer>
</Border>
</Grid>
代码
public partial class EditPanelControl : UserControl
{
public EditPanelControl()
{
InitializeComponent();
DataContext = this;
Loaded += EditPanelControl_Loaded;
}
private void EditPanelControl_Loaded(object sender, RoutedEventArgs e)
{
AddSuperTextControl();
}
private void AddSuperTextControl()
{
var stc = new SuperTextControl();
stc.SplitEvent += Stc_SplitEvent;
stc.DeleteEvent += Stc_DeleteEvent;
stc.SuperTextBox.Text = MainWindow.NextId.ToString();
MainPanel.Children.Add(stc);
}
private void Stc_DeleteEvent(object sender, EventArgs e)
{
// todo: don't allow delete if only 1 child
var stc = (SuperTextControl)sender;
MainPanel.Children.Remove(stc);
}
private void Stc_SplitEvent(object sender, EventArgs e)
{
var stc = (SuperTextControl)sender; // fyi
AddSuperTextControl();
}
}
SuperTextControl(用户控件)
XAML
<Grid Margin="5">
<Border Background="Wheat" BorderBrush="Blue" BorderThickness="1">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Stretch">
<TextBox Name="SuperTextBox" Margin="5"/>
<DockPanel LastChildFill="False" Margin="0,0,0,5">
<Button Content="Split" Click="SplitHandler" Margin="5,0" DockPanel.Dock="Left"/>
<Button Content="Delete" Click="DeleteHandler" Margin="5,0" DockPanel.Dock="Right"/>
</DockPanel>
</StackPanel>
</Border>
</Grid>
代码
public partial class SuperTextControl : UserControl
{
public event EventHandler SplitEvent;
public event EventHandler DeleteEvent;
public SuperTextControl()
{
InitializeComponent();
}
private void SplitHandler(object sender, RoutedEventArgs e)
{
var button = (Button)sender; // fyi
if (SplitEvent != null)
{
SplitEvent(this, new EventArgs());
}
}
private void DeleteHandler(object sender, RoutedEventArgs e)
{
var button = (Button)sender; // fyi
if (DeleteEvent != null)
{
DeleteEvent(this, new EventArgs());
}
}
}
我正准备编写一些桌面应用程序来模拟一些东西,我认为这是尝试一些新技术的好机会。由于应用程序适用于 Windows,并且我在某处看到 Visual Studio 社区版,因此我决定尝试 WPF。
事情是这样的。基本视图应该看起来像一个简单的网格,其中每个矩形都是一个实际的文本框。我可以在每一个中点击并写一些文字。
实现这一目标还算不错。起初我玩的是 Grid 和 ColumnDefinitions 以及 RowDefinitions,这对硬编码的那些很有用。之后,我尝试了 ItemsControl 和 ItemTemplate,就差不多了。
但是现在剧情出现了转折。我希望能够编辑每一个 TextBox。通过编辑,我的意思是将它分成几个较小的文本框。因此,如果我将第 2 个 1 分成 2 块,第 3 个 1 分成 3 块,它应该看起来像:
而且我不知道如何解决这个问题。由于它与其他的不一样,我认为我不能再将 ItemsControl 与模板一起使用(或者我可以吗?)。我是 WPF 的新手,所以也许有一些我还没有看到的明显的东西。因此,如果有人非常了解 WPF 并且可以指出正确的方向或者至少告诉我 "What are you doing? WPF is not good for that kind of apps, use XXX instead".
您可以使用 ÌtemTemplateSelector
根据变量分配不同的模板。
在您后面的代码中,我们有一个简单的 Rows
属性。这将用于显示网格中显示的行数。
MainWindow.cs
public partial class MainWindow : Window
{
public int[] Rows{ get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Rows= new int[] { 1, 2, 1, 3 };
}
}
根据Rows
DataTemplate
的ItemTemplateSelector.cs
public class RowTemplateSelecter: DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
int rows = 1;
int.TryParse(item.ToString(), out rows);
switch (rows)
{
case 1:
return element.FindResource("OneRow") as DataTemplate;
case 2:
return element.FindResource("TwoRows") as DataTemplate;
case 3:
return element.FindResource("ThreeRows") as DataTemplate;
default:
return element.FindResource("OneRow") as DataTemplate;
}
}
}
最后 MainWindow.xaml 添加 3 个模板和 ItemTemplateSelector
<Window.Resources>
<DataTemplate x:Key="OneRow">
<Grid Background="Red" Width="100" Height="100">
</Grid>
</DataTemplate>
<DataTemplate x:Key="TwoRows" >
<Grid Width="100" Height="100">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Fill="Blue"/>
<Rectangle Fill="Green" Grid.Row="1"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="ThreeRows">
<Grid Width="100" Height="100">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Fill="Yellow"/>
<Rectangle Fill="Orange" Grid.Row="1"/>
<Rectangle Fill="Black" Grid.Row="2"/>
</Grid>
</DataTemplate>
<local:RowTemplateSelecter x:Key="RowSelector"/>
</Window.Resources>
<Grid>
<ItemsControl x:Name="rectangles"
ItemsSource="{Binding Rows}"
ItemTemplateSelector="{StaticResource RowSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
希望对您有所帮助
问得好,根据您的应用程序的功能,以及如何将每个控件的数据连接到您的 app/model/whatever。
,当然有很多布局控件的方法此答案侧重于布局,同时占用所有可用 space 并允许复制每个容器中的内容。内容很好地流动,直到 window 变得太小、太窄等,这就是您需要决定您的应用程序将允许用户做什么的地方。在这段代码达到生产质量之前还有很多工作要做,但它是一些 WPF 基础知识的一个很好的例子,可以帮助您熟悉 WPF 平台。
我添加了一些边框、边距和背景颜色用于测试,这样您就可以看到哪个容器占据了哪个 space。适合测试;可能会在最终版本中删除或更改为透明。
主窗口
XAML
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Text="WPF" FontSize="36" Margin="20" Foreground="Orange" HorizontalAlignment="Center"/>
</StackPanel>
<Grid Grid.Row="1" Margin="5">
<Border Background="LightGray" BorderBrush="Red" BorderThickness="1">
<UniformGrid Columns="4" Name="MainPanel"/>
</Border>
</Grid>
</Grid>
代码
public partial class MainWindow : Window
{
private static int _nextId = 0;
public static int NextId
{
get { return _nextId++; }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// add non-multiple of 8 to see how layout works
for (var i=0; i<7; i++)
{
MainPanel.Children.Add(new EditPanelControl());
}
}
}
EditPanelControl(用户控件)
XAML
<Grid Margin="5">
<Border Background="LightYellow" BorderBrush="Green" BorderThickness="1">
<!-- Make this a viewbox if you want to show all items but have them shrink -->
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Name="MainPanel" VerticalAlignment="Center"/>
</ScrollViewer>
</Border>
</Grid>
代码
public partial class EditPanelControl : UserControl
{
public EditPanelControl()
{
InitializeComponent();
DataContext = this;
Loaded += EditPanelControl_Loaded;
}
private void EditPanelControl_Loaded(object sender, RoutedEventArgs e)
{
AddSuperTextControl();
}
private void AddSuperTextControl()
{
var stc = new SuperTextControl();
stc.SplitEvent += Stc_SplitEvent;
stc.DeleteEvent += Stc_DeleteEvent;
stc.SuperTextBox.Text = MainWindow.NextId.ToString();
MainPanel.Children.Add(stc);
}
private void Stc_DeleteEvent(object sender, EventArgs e)
{
// todo: don't allow delete if only 1 child
var stc = (SuperTextControl)sender;
MainPanel.Children.Remove(stc);
}
private void Stc_SplitEvent(object sender, EventArgs e)
{
var stc = (SuperTextControl)sender; // fyi
AddSuperTextControl();
}
}
SuperTextControl(用户控件)
XAML
<Grid Margin="5">
<Border Background="Wheat" BorderBrush="Blue" BorderThickness="1">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Stretch">
<TextBox Name="SuperTextBox" Margin="5"/>
<DockPanel LastChildFill="False" Margin="0,0,0,5">
<Button Content="Split" Click="SplitHandler" Margin="5,0" DockPanel.Dock="Left"/>
<Button Content="Delete" Click="DeleteHandler" Margin="5,0" DockPanel.Dock="Right"/>
</DockPanel>
</StackPanel>
</Border>
</Grid>
代码
public partial class SuperTextControl : UserControl
{
public event EventHandler SplitEvent;
public event EventHandler DeleteEvent;
public SuperTextControl()
{
InitializeComponent();
}
private void SplitHandler(object sender, RoutedEventArgs e)
{
var button = (Button)sender; // fyi
if (SplitEvent != null)
{
SplitEvent(this, new EventArgs());
}
}
private void DeleteHandler(object sender, RoutedEventArgs e)
{
var button = (Button)sender; // fyi
if (DeleteEvent != null)
{
DeleteEvent(this, new EventArgs());
}
}
}