WPF绑定控件到另一个控件

时间:2014-04-23 15:08:01

标签: c# wpf xaml datagrid combobox

我有以下WPF窗口。更改#Cables的值会将标签添加(或删除)到TabControl(C2,C3,C4 ......)。更改#Phases会向DataGrid添加新行。

Window

所有选项卡(除了这个选项之外)都具有相同的格式,因此我创建了一个UserControl NTab类,它有自己的.xaml和代码隐藏。

现在,每个其他选项卡都有一个ComboBox,用户可以在其中选择相应的Phase(通过name属性)。为此,NTab需要了解“选项”标签中的阶段DatGrid。我的代码目前分为两类:

  • MainWindow,其中包含整个窗口和“选项”选项卡的代码(.xaml和代码隐藏);
  • NTab,其中包含自身的代码(.xaml和代码隐藏)。

阶段DataGrid's ItemSourceObservableCollection,因此我所做的就是将集合发送到NTab构造函数并将其.CollectionChanged事件绑定到以下NTab 1}} function(阶段是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));
}

这将创建DataGrid数据的副本(只是名称)并将其存储在每个NTab内作为绑定到ComboBox的依赖项属性。这很有效。

我的问题是,是否有更好的方法来做到这一点。我宁愿不必创建数据的“冗余”副本,并希望能够将ComboBoxes直接绑定到DataGrid。这可能吗?


编辑:添加我的代码。我删除了与问题无关的.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

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

<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

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&lt;NPhase&gt;</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;
    }
}

1 个答案:

答案 0 :(得分:1)

您正在以错误的方式查看此内容...当UI控件需要了解有关其他UI控件的任何内容时,WPF中这种情况很少见。使用数据更为常见。这样,我的意思是您的NTab只需要知道阶段DataGrid中的数据而不是控件。

实现此目标的最简单方法是为整个TabControl提供一个视图模型,该模型在各种TabItem上共享其属性。因此,不要试图处理UI事件以保持集合相同,只需为所有TabItem使用一个集合。在DataGrid中,您可以这样做:

<DataGrid ItemsSource="{Binding YourCollection}" ... />

在每个额外的TabItem上,你可以拥有:

<ComboBox ItemsSource="{Binding YourCollection}" ... />

通过这种方式,一个TabItem中对集合的更新将自动反映在所有其他TabItem中,而无需您执行任何操作。