Wpf 在ItemsControl中设置单个CustomControls的名称和访问权限
我正在用C、.NET4.6和WPF编写一个程序。我希望在运行时以二维网格大小动态指定一组CustomControl,并能够访问每个CustomControl 我做了一些研究,找到了关于ItemsControl的不同信息,并创建了一个解决方案,在某种程度上满足了我的需要。 下面是代码的相关部分,它们编译并运行 自定义控件的XAMLWpf 在ItemsControl中设置单个CustomControls的名称和访问权限,wpf,Wpf,我正在用C、.NET4.6和WPF编写一个程序。我希望在运行时以二维网格大小动态指定一组CustomControl,并能够访问每个CustomControl 我做了一些研究,找到了关于ItemsControl的不同信息,并创建了一个解决方案,在某种程度上满足了我的需要。 下面是代码的相关部分,它们编译并运行 自定义控件的XAML <UserControl x:Class="TestApp.MyUserControl" xmlns="http://schemas.m
<UserControl x:Class="TestApp.MyUserControl"
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:local="clr-namespace:TestApp"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Rectangle Fill="{Binding MyFill1, RelativeSource={RelativeSource FindAncestor, AncestorType=local:MyUserControl}}">
</Rectangle>
<Viewbox>
<TextBlock Text="{Binding MyText1, RelativeSource={RelativeSource FindAncestor, AncestorType=local:MyUserControl}}" >
</TextBlock>
</Viewbox>
</Grid>
</UserControl>
namespace TestApp
{
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty MyText1Property =
DependencyProperty.Register("MyText1",
typeof(String), typeof(MyUserControl),
new PropertyMetadata(""));
public String MyText1
{
get { return (String)GetValue(MyText1Property); }
set { SetValue(MyText1Property, value); }
}
public static readonly DependencyProperty MyFill1Property =
DependencyProperty.Register("MyFill1",
typeof(SolidColorBrush),
typeof(MyUserControl),
new PropertyMetadata(new SolidColorBrush(Colors.Green)));
public SolidColorBrush MyFill1
{
get { return (SolidColorBrush)GetValue(MyFill1Property); }
set { SetValue(MyFill1Property, value); }
}
public MyUserControl()
{
InitializeComponent();
}
}
}
用于托管主窗口的XAML
<Window x:Class="TestApp.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:TestApp"
mc:Ignorable="d"
Name="MyMainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ItemsControl Name="MyItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding ElementName=MyMainWindow, Path=UniformGridColumns, Mode=OneWay}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyUserControl MyText1="{Binding Text1}" MyFill1="{Binding Fill1}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
托管主窗口的代码隐藏
namespace TestApp
{
public partial class MainWindow : Window
{
public int UniformGridColumns //number of columns of the grid
{
get { return (int)GetValue(UniformGridColumnsProperty); }
set { SetValue(UniformGridColumnsProperty, value); }
}
public static readonly DependencyProperty UniformGridColumnsProperty =
DependencyProperty.Register("UniformGridColumns", typeof(int), typeof(MainWindow),
new FrameworkPropertyMetadata((int)0));
public MainWindow()
{
InitializeComponent();
//this.DataContext = this;
Setup(13, 5); //13 columns, 5 rows
}
public void Setup(int columns, int rows) //setup the grid
{
UniformGridColumns = columns;
SingleControl[] singleControls = new SingleControl[rows*columns];
for (int i = 0; i < rows*columns; i++)
singleControls[i] = new SingleControl()
{
Text1 = (i/ columns + 1) + ", " + (i % columns + 1),
Fill1 = new SolidColorBrush((i % 2 != 0) ? Colors.Yellow : Colors.Red)
}; //example, display position in grid and fill with two different colours
MyItemsControl.ItemsSource = singleControls.ToList<SingleControl>();
}
public MyUserControl GetSingleControl(int column, int row) //access a single control
{
//some code involving VisualTreeHelper
return null;
}
private class SingleControl //helper class for setting up the grid
{
public String Text1 { get; set; }
public Brush Fill1 { get; set; }
}
}
}
方法MainWindow.Setupint,int用所需数量的MyCustomControls填充ItemControl,我可以标记它们并用我想要的任何颜色填充它们
问题1:
如何实现在指定位置返回MyCustomControl的GetSingleControlint、int?我从一个涉及VisualTreeHelper的解决方案开始,这个解决方案看起来很笨拙而且不灵活
问题2:
如何为第1行和第5列中的项目设置所有MyCustomControl的名称,例如类似MyCustomControl_01_05的名称
问题3:
如果问题1和2无法根据我的解决方案得到回答,那么什么是更合适的方法
谢谢大家! 举一个elgonzo和Andy都说的例子,你应该把事情改得更友好。一旦你做了更多的研究,你就会明白为什么你不想为dependencProperties、绑定到后面的代码以及手动编码所有添加的UserControl而烦恼。 这可以变得美观或更精简,但我编写它是为了给出一个完整的示例,说明如何使用MVVM实现这一点。我试图使它简单和基本,同时演示如何重构您的想法 新建MainWindow.xaml
<Window x:Class="TestApp.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:TestApp"
d:DataContext="{d:DesignInstance {x:Type local:MainWindowViewModel}}"
mc:Ignorable="d"
Name="MyMainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<ItemsControl Name="MyItemsControl" ItemsSource="{Binding MyList}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding GridRow}" />
<Setter Property="Grid.Column" Value="{Binding GridColumn}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding ColumnCount}" Rows="{Binding RowCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Fill="{Binding Fill1}"/>
<TextBlock Text="{Binding Text1}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
添加MainWindowViewModel.cs文件:
-注意:如果需要,MyElement可以抽象为UserControl的viewmodel
public class MyElement : INotifyPropertyChanged
{
public MyElement()
{
//some default data for design testing
Text1 = "Test";
Fill1 = new SolidColorBrush(Colors.Red);
GridColumn = 13;
GridRow = 5;
}
private string _text1;
public string Text1
{
get { return _text1; }
set{
if (value != _text1) { _text1 = value; RaisePropertyChanged(); }
}
}
private Brush _fill1;
public Brush Fill1
{
get { return _fill1; }
set
{
if (value != _fill1) { _fill1 = value; RaisePropertyChanged(); }
}
}
private int _gridRow;
public int GridRow
{
get { return _gridRow; }
set
{
if (value != _gridRow) { _gridRow = value; RaisePropertyChanged(); }
}
}
private int _gridColumn;
public int GridColumn
{
get { return _gridColumn; }
set
{
if (value != _gridColumn) { _gridColumn = value; RaisePropertyChanged(); }
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel() : this(13, 5) { }
public MainWindowViewModel(int columns, int rows)
{
ColumnCount = columns;
RowCount = rows;
MyList = new ObservableCollection<MyElement>();
//your original setup code
for (int i = 0; i < columns; i++)
{
for (int j = 0; j < rows; j++)
{
var vm = new MyElement
{
Text1 = (i / columns + 1) + ", " + (i % columns + 1),
Fill1 = new SolidColorBrush((i % 2 != 0) ? Colors.Yellow : Colors.Red),
GridColumn = i,
GridRow = j
};
MyList.Add(vm);
}
}
}
private int _rowCount;
public int RowCount
{
get { return _rowCount; }
set
{
if (value != _rowCount) { _rowCount = value; RaisePropertyChanged(); }
}
}
private int _columnCount;
public int ColumnCount
{
get { return _columnCount; }
set
{
if (value != _columnCount) { _columnCount = value; RaisePropertyChanged(); }
}
}
public ObservableCollection<MyElement> MyList { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
我做了一个更完整的解决方案,它使用INotifyPropertyChanged。我不会解释在你不知道的情况下使用它的原因,因为有更好的解释你可以快速搜索
我还使所有的动态信息都使用绑定,使事情更容易更改。现在,网格大小和项目定位已绑定到数据。因此,它应该在您更改MyElement时自动调整
这将为您重构代码提供一个良好的起点,并帮助您利用WPF的设计目的,因为内置了许多机制,因此您不必像编写代码时那样硬编码UI层操作
这也回答了您的问题:
Q1:您现在可以访问MyElement列表并相应地更改它们。当您更改任何内容时,UI层应自动更新
问题2:现在不需要这样做,因为每个MyElement都将为其网格位置保留一个属性。因此,您可以直接访问它 想要命名和访问ItemsControl中的某些控件,如果没有错的话,听起来很糟糕。通常不直接在代码隐藏中访问和管理GUI元素,而是将GUI元素绑定到ItemsControl.ItemsSource集合中的某些视图模型项,在代码隐藏中操纵这些视图模型,WPF的绑定机制将允许GUI元素响应/更新,而您的代码不会关心GUI元素的肮脏细节、复杂动态和层次结构/布局。您应该将研究扩展到mvvm。这就是elgonzo所描述的。您的依赖项属性看起来可能不必要。例如,usercontrol已经有一个可以绑定的后台属性。您也可以只使用datatemplate输出UI,而不使用usercontrol。谢谢。我已经怀疑MVVM在这里是个好主意。我犹豫了一下,因为这是一个开销,这只是一个小项目。事实上,这是一个很好的起点,正是我所期望的。非常感谢。
public class MyElement : INotifyPropertyChanged
{
public MyElement()
{
//some default data for design testing
Text1 = "Test";
Fill1 = new SolidColorBrush(Colors.Red);
GridColumn = 13;
GridRow = 5;
}
private string _text1;
public string Text1
{
get { return _text1; }
set{
if (value != _text1) { _text1 = value; RaisePropertyChanged(); }
}
}
private Brush _fill1;
public Brush Fill1
{
get { return _fill1; }
set
{
if (value != _fill1) { _fill1 = value; RaisePropertyChanged(); }
}
}
private int _gridRow;
public int GridRow
{
get { return _gridRow; }
set
{
if (value != _gridRow) { _gridRow = value; RaisePropertyChanged(); }
}
}
private int _gridColumn;
public int GridColumn
{
get { return _gridColumn; }
set
{
if (value != _gridColumn) { _gridColumn = value; RaisePropertyChanged(); }
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel() : this(13, 5) { }
public MainWindowViewModel(int columns, int rows)
{
ColumnCount = columns;
RowCount = rows;
MyList = new ObservableCollection<MyElement>();
//your original setup code
for (int i = 0; i < columns; i++)
{
for (int j = 0; j < rows; j++)
{
var vm = new MyElement
{
Text1 = (i / columns + 1) + ", " + (i % columns + 1),
Fill1 = new SolidColorBrush((i % 2 != 0) ? Colors.Yellow : Colors.Red),
GridColumn = i,
GridRow = j
};
MyList.Add(vm);
}
}
}
private int _rowCount;
public int RowCount
{
get { return _rowCount; }
set
{
if (value != _rowCount) { _rowCount = value; RaisePropertyChanged(); }
}
}
private int _columnCount;
public int ColumnCount
{
get { return _columnCount; }
set
{
if (value != _columnCount) { _columnCount = value; RaisePropertyChanged(); }
}
}
public ObservableCollection<MyElement> MyList { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}