【问题标题】:Dynamically update DataGridComboBoxColumn (not MVVM)动态更新 DataGridComboBoxColumn(不是 MVVM)
【发布时间】:2016-04-07 00:12:42
【问题描述】:

我有一个 DataGrid,它的源是基于数据库的。代码不在 MVVM 中。

现在,我需要根据同一 DataGrid 中的不同 DataGridComboBoxColumn 值更改 DataGridComboBoxColumn 源 - 我确信有一个简单的解决方案,但仍然无法弄清楚 - 我该怎么做?

我的代码:

XAML:

<DataGridComboBoxColumn x:Name="active_idnt_deviceCmb"  SelectedValueBinding="{Binding idnt_linked_io_device}" DisplayMemberPath="correct_idnt_active_logic_device" SelectedValuePath="idnt_active_device" Header="input id" Width="80"></DataGridComboBoxColumn>
    <DataGridComboBoxColumn x:Name="active_device_addressCmb" ElementStyle="{StaticResource MyComboBoxStyle}"  SelectedValueBinding="{Binding idnt_linked_io_device}" DisplayMemberPath="active_device_address" SelectedValuePath="active_device_address" Header="Relay Address" Width="65"><DataGridComboBoxColumn.EditingElementStyle>
            <Style TargetType="{x:Type ComboBox}">
                <EventSetter Event="SelectionChanged" Handler="changeDeviceAddress" />
            </Style>
    </DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>

cs:

private void changeDeviceAddress(object sender, SelectionChangedEventArgs e)
{
    HelpDataSet.ACTIVE_IO_DEVICESDataTable dtActiveIo = new HelpDataSet.ACTIVE_IO_DEVICESDataTable();
    var comboBox = sender as ComboBox;

    if (comboBox.SelectedValue != null)
    {
        AppHelp.ActiveIODeviceAdapter.ClearBeforeFill = true;
        AppHelp.ActiveIODeviceAdapter.FillByIdntRelayAddress(dtActiveIo, comboBox.SelectedValue.ToString());
        active_idnt_deviceCmb.ItemsSource = dtActiveIo.DefaultView;
    }
}

但它会更改整个列源,而不仅仅是行中的特定单元格。

【问题讨论】:

  • 删除所有内容并使用正确的 DataBinding。
  • @HighCore 我该怎么做?
  • 您可能有兴趣阅读Data Binding Overview 它将解释如何利用您的DataContext 来发挥您的优势。
  • @DasDas 你能解释一下为什么在另一个 DataGridComboBoxColumn 中有 DataGridComboBoxColumn 吗?当 active_idnt_deviceCmb 发生变化时,是否要更改 active_device_addressCmb 的来源?
  • @Ilan 我想在 active_device_addressCmb 更改时更改 active_idnt_deviceCmb 的来源

标签: c# sql .net wpf wpfdatagrid


【解决方案1】:

这是一个完全不使用 MVVM 方法的解决方案,它基于 WPF 面向选择的行为。请记住,所有模型都只显示数据网格行,它们没有任何附加功能。源集合收集由组合选择触发器在行为中触发。 这是代码: 1. XAML 代码:

<Window x:Class="ComboWithoutCodeBehind.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:comboWithoutCodeBehind="clr-namespace:ComboWithoutCodeBehind"
    Title="MainWindow" Height="350" Width="525" x:Name="This">
    <Grid >
        <DataGrid x:Name="SelectDataGrid"
                  ItemsSource="{Binding ElementName=This, Path=Persons}" HorizontalAlignment="Left" VerticalAlignment="Top" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridCheckBoxColumn x:Name="dgCheckBox" Header="Select" Width="45" Binding="{Binding IsChecked}"/>
                <DataGridTextColumn Header="FIRST NAME" Width="125" Binding="{Binding FNAME}"/>
                <DataGridTextColumn Header="LAST NAME" Width="125" Binding="{Binding LNAME}"/>
                <DataGridTemplateColumn Header="Selection" Width="120">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition></ColumnDefinition>
                                    <ColumnDefinition></ColumnDefinition>
                                </Grid.ColumnDefinitions>
                                <ComboBox Grid.Column="0"  x:Name="DataGridTemplateColumnComboBox" SelectedIndex="0" ItemsSource="{Binding ElementName=This, Path=ServersCollection}" 
                                      DisplayMemberPath="ServerName"></ComboBox>
                                <ComboBox Grid.Column="1" DisplayMemberPath="DbName">
                                    <i:Interaction.Behaviors>
                                        <comboWithoutCodeBehind:ItemsSourcePosessingBehavior 
                                        SourceProvidingFactory="{Binding ElementName=This, Path=MainSourceProvidingFactory}"
                                            SiblingComboBox="{Binding ElementName=DataGridTemplateColumnComboBox}"/>
                                    </i:Interaction.Behaviors>
                                </ComboBox>
                            </Grid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>

2. 后面的代码、型号和工厂代码:

public partial class MainWindow : Window
{
    public static readonly DependencyProperty PersonsProperty = DependencyProperty.Register("Persons",
        typeof (ObservableCollection<Person>), typeof (MainWindow),
        new PropertyMetadata(default(ObservableCollection<Person>)));

    public static readonly DependencyProperty MainSourceProvidingFactoryProperty =
        DependencyProperty.Register("MainSourceProvidingFactory", typeof (SourceProvidingFactory),
            typeof (MainWindow), new PropertyMetadata(default(SourceProvidingFactory)));

    public ObservableCollection<Person> Persons
    {
        get { return (ObservableCollection<Person>)GetValue(PersonsProperty); }
        set { SetValue(PersonsProperty, value); }
    }

    public SourceProvidingFactory MainSourceProvidingFactory
    {
        get { return (SourceProvidingFactory)GetValue(MainSourceProvidingFactoryProperty); }
        set { SetValue(MainSourceProvidingFactoryProperty, value); }
    }

    public MainWindow()
    {
        MainSourceProvidingFactory = new SourceProvidingFactory(GetCollection);
        InitSources();
        InitializeComponent();
    }

    private ObservableCollection<DbDetails> GetCollection(object arg)
    {
        //you can perform your db relate logic here
        var sereverDetails = arg as ServerDetails;
        return sereverDetails == null ? null : new ObservableCollection<DbDetails>(sereverDetails.DbDetailses);
    }


    private void InitSources()
    {
        var l = new List<Person>
        {
            new Person {FNAME = "John", LNAME = "W"},
            new Person {FNAME = "George", LNAME = "R"},
            new Person {FNAME = "Jimmy", LNAME = "B"},
            new Person {FNAME = "Marry", LNAME = "B"},
            new Person {FNAME = "Ayalot", LNAME = "A"},
        };
        Persons = new ObservableCollection<Person>(l);

        ServersCollection = new ObservableCollection<ServerDetails>(new List<ServerDetails>
        {
            new ServerDetails
            {
                ServerName = "A",
                DbDetailses = new List<DbDetails>
                {
                    new DbDetails {DbName = "AA"},
                    new DbDetails {DbName = "AB"},
                    new DbDetails {DbName = "AC"},
                }
            },
            new ServerDetails
            {
                ServerName = "B",
                DbDetailses = new List<DbDetails>
                {
                    new DbDetails {DbName = "BA"},
                    new DbDetails {DbName = "BB"},
                    new DbDetails {DbName = "BC"},
                }
            },
            new ServerDetails
            {
                ServerName = "C",
                DbDetailses = new List<DbDetails>
                {
                    new DbDetails {DbName = "CA"},
                    new DbDetails {DbName = "CB"},
                }
            }
        });
    }


    public ObservableCollection<ServerDetails> ServersCollection { get; set; }
}

public class SourceProvidingFactory
{
    public SourceProvidingFactory(Func<object, ObservableCollection<DbDetails>> action)
    {
        GetCollection = action;
    }

    public Func<object, ObservableCollection<DbDetails>> GetCollection { get; set; }
}

public class Person : BaseObservableObject
{
    private string _lName;
    private string _fName;
    private bool _checked;

    public bool IsChecked
    {
        get { return _checked; }
        set
        {
            _checked = value;
            OnPropertyChanged();
        }
    }

    public string LNAME
    {
        get { return _lName; }
        set
        {
            _lName = value;
            OnPropertyChanged();
        }
    }

    public string FNAME
    {
        get { return _fName; }
        set
        {
            _fName = value;
            OnPropertyChanged();
        }
    }
}

public class ServerDetails : BaseObservableObject
{
    private string _serverName;

    public string ServerName
    {
        get { return _serverName; }
        set
        {
            _serverName = value;
            OnPropertyChanged();
        }
    }

    public List<DbDetails> DbDetailses { get; set; }
}

public class DbDetails : BaseObservableObject
{
    private string _dbName;

    public string DbName
    {
        get { return _dbName; }
        set
        {
            _dbName = value;
            OnPropertyChanged();
        }
    }
}

3。行为代码:

public class ItemsSourcePosessingBehavior : Behavior<ComboBox>
{

    public static readonly DependencyProperty SiblingComboBoxProperty = DependencyProperty.Register(
        "SiblingComboBox", typeof(ComboBox), typeof(ItemsSourcePosessingBehavior), new PropertyMetadata(default(ComboBox)));

    public ComboBox SiblingComboBox
    {
        get { return (ComboBox)GetValue(SiblingComboBoxProperty); }
        set { SetValue(SiblingComboBoxProperty, value); }
    }

    public static readonly DependencyProperty SourceProvidingFactoryProperty = DependencyProperty.Register(
        "SourceProvidingFactory", typeof (SourceProvidingFactory), typeof (ItemsSourcePosessingBehavior), new PropertyMetadata(default(SourceProvidingFactory)));

    public SourceProvidingFactory SourceProvidingFactory
    {
        get { return (SourceProvidingFactory) GetValue(SourceProvidingFactoryProperty); }
        set { SetValue(SourceProvidingFactoryProperty, value); }
    }


    protected override void OnAttached()
    {
        base.OnAttached();
        SiblingComboBox.SelectionChanged += SiblingComboBoxOnSelectionChanged;
        SiblingComboBox.Loaded += SiblingComboBoxOnLoaded;
    }

    private void SiblingComboBoxOnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        AssociatedObject.ItemsSource = null;
        var siblingCombo = sender as ComboBox;
        InitAssociatedObjectItemsSource(siblingCombo);
    }


    private void SiblingComboBoxOnSelectionChanged(object sender, SelectionChangedEventArgs selectionChangedEventArgs)
    {
        AssociatedObject.ItemsSource = null;
        var siblingCombo = sender as ComboBox;
        InitAssociatedObjectItemsSource(siblingCombo);
    }

    private void InitAssociatedObjectItemsSource(ComboBox siblingCombo)
    {
        if (siblingCombo == null)
        {
            return;
        }
        if (SourceProvidingFactory == null)
        {
            return;
        }
        AssociatedObject.ItemsSource = SourceProvidingFactory.GetCollection(siblingCombo.SelectedItem);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        SiblingComboBox.SelectionChanged -= SiblingComboBoxOnSelectionChanged;
        SiblingComboBox.Loaded -= SiblingComboBoxOnLoaded;
    }
}

这是一个完整的解决方案,只需复制/粘贴... 如果您对代码有任何问题,我很乐意为您提供帮助。

问候。

【讨论】:

  • 我不希望它是 MVVM
  • @DasDas 请查看新的解决方案,它基于 wpf 行为和代码隐藏方法。
【解决方案2】:

我认为这是我们在这种情况下面临的一个非常基本的问题。现在由于您对显示和绑定没有问题,所以我将写下我认为的问题。

不要使用 active_idnt_deviceCmb.ItemsSource 这将重置所有 Combobox 项目源。您希望根据其他列值在不同行中使用不同的选项。 因此,您需要为每条记录与每个下拉菜单保持一对一的关系。

做到这一点的最佳方法是将任何类绑定到您需要创建这么多数据源的每条记录。如果List&lt;MyRecord&gt; 是网格的 itemsource 则设计类如下:

public class MyRecord : INotifyPropertyChanged
{      
    private string a;
    public string A
    {
        get { return a; }
        set { a = value;
        OnPropertyChanged("A");
        OnPropertyChanged("SecondList");
        }
    }

    private string b;
    public string B
    {
        get { return b; }
        set { b = value;
        OnPropertyChanged("B");
        }
    }

    private List<ComboBoxItem> firstList;
    public List<ComboBoxItem> FirstList
    {
        get { return firstList; }
        set { firstList = value;
        OnPropertyChanged("FirstList");
        }
    }

    private List<ComboBoxItem> secondList;
    public List<ComboBoxItem> SecondList
    {
        get { return secondList.Where(x=>x.value.StartsWith(A)).ToList(); }
        set { secondList = value;
        OnPropertyChanged("SecondList");
        }
    }


}

PS: 网格的第一列与 A 绑定,第二列与 B 绑定,并且 firstlist 和 secondlist 绑定到两列的下拉列表。每次我选择第一列中的值时,它都会反射回属性 A,此时我正在刷新第二个列表(其中也包含过滤器逻辑,或者您可以调用过滤器逻辑并单独刷新第二个列表)。

由于第二个列表只绑定了一条记录,因此只有一个下拉菜单会重置。

XAML 代码理解:

<DataGridComboBoxColumn x:Name="active_idnt_deviceCmb"
                                    SelectedValuePath="value"
                                    DisplayMemberPath="display"                                         
                                    SelectedValueBinding="{Binding A}"                                        
                                    Header="input id" Width="80">
                <DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="{x:Type ComboBox}">
                        <Setter Property="ItemsSource" Value="{Binding Path=FirstList}" />   
                    </Style>
                   </DataGridComboBoxColumn.EditingElementStyle>
            </DataGridComboBoxColumn>
            <DataGridComboBoxColumn  
                                    SelectedValuePath="value"
                                    DisplayMemberPath="display"                                        
                                    SelectedValueBinding="{Binding B}"
                                    Header="Relay Address" Width="65">
                <DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="{x:Type ComboBox}">
                        <Setter Property="ItemsSource" Value="{Binding Path=SecondList}" />                                  
                    </Style>
                </DataGridComboBoxColumn.EditingElementStyle>
            </DataGridComboBoxColumn>

Combobox项源类(只是为了理解DataGridComboBoxColumn绑定):

public class ComboBoxItem
{
    public ComboBoxItem(string a)
    {
        display = value = a;
    }

    public string display { get; set; }

    public string value { get; set; }
}

希望您能够将此代码安装到您的应用程序中。最重要的是一对一的关系。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 2011-07-27
    相关资源
    最近更新 更多