[英]WPF Binding control to another control
I have the following WPF window. 我有以下WPF窗口。 Changing the value of #Cables adds (or removes) tabs to the
TabControl
(C2, C3, C4...). 更改#Cables的值会向
TabControl
(C2,C3,C4 ...)添加(或删除)标签。 Changing #Phases adds new rows to the DataGrid
. 更改#Phase会向
DataGrid
添加新行。
All of the tabs (other than this Options one) have the same format, so I created a UserControl NTab
class which has its own .xaml and code-behind. 所有选项卡(除了“选项”之外)均具有相同的格式,因此我创建了一个
UserControl NTab
类,该类具有自己的.xaml和代码后置。
Now, each of the other tabs will have a ComboBox
, where the user selects the appropriate Phase (by the name property). 现在,每个其他选项卡将具有一个
ComboBox
,用户可以在其中选择适当的“阶段”(通过name属性)。 For that to be possible, the NTab
needs to be aware of the Phase DatGrid
in the Options tab. 为此,
NTab
需要在“选项”选项卡中了解Phase DatGrid
。 My code is currently split into two classes: 我的代码当前分为两类:
MainWindow
, which contains the code (.xaml and code-behind) for the window as a whole and for the Options Tab; MainWindow
,包含整个窗口和“选项”选项卡的代码(.xaml及其后置代码); NTab
, which contains the code (.xaml and code-behind) for itself. NTab
,其NTab
包含代码(.xaml和背后代码)。 The Phase DataGrid's ItemSource
is an ObservableCollection
, so what I've done is sent the collection to the NTab
constructor and tied its .CollectionChanged
event to the following NTab
function (Phases is an ObservableCollection<string>
dependency property): 该阶段
DataGrid's ItemSource
是ObservableCollection
,我这样做有什么被送到收集到NTab
构造和它并列.CollectionChanged
事件以下NTab
功能(阶段是一个ObservableCollection<string>
依赖属性):
public void PhasesChanged(object source, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
var pC = source as ObservableCollection<NPhase>;
Phases.Clear();
for (int i = 0; i < pC.Count; i++)
Phases.Add("" + (i + 1));
}
This creates a copy of the DataGrid's data (just the names) and stores it inside each NTab
as a dependency property bound to the ComboBox
. 这将创建DataGrid数据的副本(只是名称),并将其作为绑定到
ComboBox
的依赖项属性存储在每个NTab
中。 This works. 这可行。
My question is if there is a better way to do this. 我的问题是,是否有更好的方法可以做到这一点。 I would rather not have to create "redundant" copies of data and would like to be able to bind the
ComboBoxes
directly to the DataGrid
. 我宁愿不必创建数据的“冗余”副本,也希望能够将
ComboBoxes
直接绑定到DataGrid
。 Is this possible? 这可能吗?
EDIT: Adding my code. 编辑:添加我的代码。 I've deleted parts of the .xaml and code-behind which weren't relevant to the question.
我已删除了与该问题无关的.xaml和代码隐藏部分。
MainWindow.xaml MainWindow.xaml
<Window x:Class="WPF.MainWindow"
x:Name="Main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:l="clr-namespace:WPF"
Title="WPF" SizeToContent="WidthAndHeight">
<TabControl Name="tabControl" SelectedIndex="1">
<TabItem Name="Options">
<TabItem.Header>
<TextBlock>Options</TextBlock>
</TabItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<!-- ... -->
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<!-- ... -->
</Grid.ColumnDefinitions>
<Label Grid.Column="0"># Cables</Label>
<xctk:IntegerUpDown Name="nCables"
Grid.Column="1"
Minimum="1"
Value="1"
ValueChanged="nCablesChanged"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<!-- ... -->
</Grid.ColumnDefinitions>
<Label Grid.Column="0"># Phases</Label>
<xctk:IntegerUpDown Name="nPhases"
Grid.Column="1"
Minimum="1"
Value="4"
ValueChanged="nPhasesChanged"/>
</Grid>
<l:NDataGrid Grid.Row="2"
x:Name="PhaseGrid"
ItemsSource="{Binding Phases, ElementName=Main}"
LoadingRow="RowIndex"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserSortColumns="False"
VerticalScrollBarVisibility="Hidden"
Block.TextAlignment="Center"/>
</Grid>
</TabItem>
</TabControl>
</Window>
MainWindow.xaml.cs MainWindow.xaml.cs
public partial class MainWindow : Window
{
ObservableCollection<NPhase> Phases;
public static DependencyProperty PhasesProperty = DependencyProperty.Register("Phases", typeof(ICollectionView), typeof(MainWindow));
public ICollectionView IPhasesCollection
{
get { return (ICollectionView)GetValue(PhasesProperty); }
set { SetValue(PhasesProperty, value); }
}
/// <summary>Controls the number of cables to be created</summary>
/// <param name="sender">(IntegerUpDown)nCables</param>
/// <param name="e">not used</param>
private void nCablesChanged(object sender, RoutedEventArgs e)
{
int n = tabControl.Items.Count - 1;
var o = sender as IntegerUpDown;
int v = (int)o.Value;
if (v > n)
{
for (int i = n; i < v; i++)
{
TabItem tab = new TabItem();
tab.Header = "C" + (i + 1);
tab.Content = new NTab(Phases);
tabControl.Items.Add(tab);
}
}
else if (v < n)
{
for (int i = v; i < n; i++)
tabControl.Items.RemoveAt(n);
}
}
/// <summary>Modifies the DataGrid according to the number of phases desired</summary>
/// <param name="sender">(IntegerUpDown)nPhases</param>
/// <param name="e">not used</param>
private void nPhasesChanged(object sender, RoutedEventArgs e)
{
//...
}
/// <summary>Sets up the row headers</summary>
/// <param name="sender">not used</param>
/// <param name="e">Row to be modified</param>
private void RowIndex(object sender, DataGridRowEventArgs e)
{
e.Row.Header = (e.Row.GetIndex() + 1).ToString();
}
public MainWindow()
{
Phases = new ObservableCollection<NPhase>();
Phases.Add(new NPhase(3, "Protensao Inicial"));
Phases.Add(new NPhase(28, "Carga Movel"));
Phases.Add(new NPhase(365, "1 Ano"));
Phases.Add(new NPhase(18250, "Vida Util"));
IPhasesCollection = CollectionViewSource.GetDefaultView(Phases);
InitializeComponent();
}
}
NTab.xaml NTab.xaml
<UserControl x:Class="WPF.NTab"
x:Name="CableTab"
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:charting="clr-namespace:System.Windows.Forms.DataVisualization.Charting;assembly=System.Windows.Forms.DataVisualization"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:l="clr-namespace:WPF"
mc:Ignorable="d" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<!-- ... -->
</Grid.RowDefinitions>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<!-- ... -->
</Grid.ColumnDefinitions>
<Label Grid.Column="2">Pull Phase</Label>
<ComboBox Grid.Column="3"
Name="Phase"
ItemsSource="{Binding Phases, ElementName=CableTab}"/>
</Grid>
</Grid>
</UserControl>
NTab.xaml.cs NTab.xaml.cs
public partial class NTab : UserControl
{
ObservableCollection<string> Phases;
public static DependencyProperty PhasesProperty = DependencyProperty.Register("Phases", typeof(ICollectionView), typeof(NTab));
public ICollectionView IPhasesCollection
{
get { return (ICollectionView)GetValue(PhasesProperty); }
set { SetValue(PhasesProperty, value); }
}
/// <summary>Updates the tab's Phase list to match the list in the Options tab.</summary>
/// <param name="source">(ObservableCollection<NPhase></param>
/// <param name="e">not used.</param>
public void PhasesChanged(object source, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
var pC = source as ObservableCollection<NPhase>;
Phases.Clear();
for (int i = 0; i < pC.Count; i++)
Phases.Add("" + (i + 1));
}
public NTab(ObservableCollection<NPhase> p)
{
InitializeComponent();
Phases = new ObservableCollection<string>();
IPhasesCollection = CollectionViewSource.GetDefaultView(Phases);
PhasesChanged(p, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
p.CollectionChanged += PhasesChanged;
}
}
You're looking at this in the wrong way... it is a rare situation in WPF when a UI control needs to know anything about another UI control. 您正在以错误的方式看待这个问题……当一个UI控件需要了解有关另一个UI控件的任何信息时,这在WPF中是一种罕见的情况。 It is much more common to work with the data .
处理数据更为常见。 By this, I mean that your
NTab
only needs to be aware of the data from the Phase DataGrid
and not the control. 借此,我的意思是您的
NTab
只需要知道Phase DataGrid
中的数据 ,而不是控件中的数据。
The simplest way to achieve this is to have one view model for the whole TabControl
which shares its properties over the various TabItem
s. 实现此目的的最简单方法是为整个
TabControl
使用一个视图模型,该视图模型在各种TabItem
共享其属性。 So rather than trying to handle UI events to keep the collections the same, just use one collection for all of the TabItem
s. 因此,与其尝试处理UI事件以使集合保持相同,不如对所有
TabItem
使用一个集合。 In the DataGrid
, you could do this: 在
DataGrid
,您可以执行以下操作:
<DataGrid ItemsSource="{Binding YourCollection}" ... />
On each additional TabItem
, you could have this: 在每个其他
TabItem
,您都可以这样做:
<ComboBox ItemsSource="{Binding YourCollection}" ... />
In this way, an update to the collection in one TabItem
will automatically be reflected in all of the other TabItem
s, without you having to do anything. 这样,一个
TabItem
集合的更新将自动反映在所有其他TabItem
,而您无需执行任何操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.