Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
502 views
in Technique[技术] by (71.8m points)

.net - Dependent Column in DataGrid not change when some other changes ( WPF C#)

I have bunch of Orders in my application which i want to show in datagrid and edit their attributes inplace. There is a class named Order which weight attribute depends on Item.Weight and Order.Count. In the datagrid to which ObservableCollection<Order> Orders is bound, I want to change rows in place. The City and Item values are changing but Weight does not update in UI when Item or Count change.

--MyViewModel

public class Order : INotifyPropertyChanged
{
    private int _OrderId;

    public int OrderId
    {
        get { return _OrderId; }
        set
        {
            _OrderId = value;
            RaiseProperChanged();
        }
    }
    private City _City;
    public City City
    {
        get { return _City; }
        set
        {
            _City = value;
            RaiseProperChanged();
        }
    }
    private Item _Item;
    public Item Item
    {
        get { return _Item; }
        set
        {
            _Item = value;
            RaiseProperChanged();
        }
    }
    private int _Count;
    public int Count
    {
        get { return _Count; }
        set
        {
            _Count = value;
            RaiseProperChanged();
        }
    }
    private int _Weight;
    public int Weight
    {
        get { return _Weight; }
        set
        {
            _Weight = value;
            RaiseProperChanged();
        }
    }
    public static ObservableCollection<Order> GetOrders()
    {
        var Orders = new ObservableCollection<Order>();
        return Orders;
    }
    public DateTime DateOfOrder { set; get; }
    public Order()
    {

    }
    public Order(int _id, City _cty, Item _itm, int _count)
    {
        OrderId = _id;
        _City = _cty;
        _Item = _itm;
        Count = _count;
        Weight = _itm.Weight * _count;
        DateOfOrder = DateTime.Now;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaiseProperChanged([CallerMemberName] string caller = "")
    {

        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
        }
    }
}

}

--My View

<DataGrid x:Name="Orders"
              FlowDirection="RightToLeft"
              Margin="20,0,20,0"
              AutoGenerateColumns="False"
              DataGridCell.Selected="DataGrid_GotFocus"
              CanUserAddRows="True"
              SelectionUnit="FullRow"
              Height="250">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding OrderId, 
                    Mode=TwoWay, 
                    UpdateSourceTrigger=PropertyChanged}"
                                Header="????"
                                FontFamily="{StaticResource BLotus}"
                                Width="70"
                                IsReadOnly="True"/>

            <DataGridComboBoxColumn SelectedItemBinding="{Binding City, 
                    Mode=TwoWay, 
                    UpdateSourceTrigger=PropertyChanged}"
                                    DisplayMemberPath="Name"
                                    x:Name="citytoadd"
                                    Header="???"
                                    Width="150"
                                    />
            <DataGridComboBoxColumn Header="?????"
                                    x:Name="itemtoadd"
                                    SelectedItemBinding="{Binding Item, 
                    Mode=TwoWay, 
                    UpdateSourceTrigger=PropertyChanged}"
                                    DisplayMemberPath="Name"
                                    Width="350" />
            <DataGridTextColumn Binding="{Binding Count, 
                    Mode=TwoWay, 
                    UpdateSourceTrigger=PropertyChanged}"
                                Header="?????"
                                Width="75"
                                />
            <DataGridTextColumn Binding="{Binding Weight, 
                    Mode=TwoWay, 
                    UpdateSourceTrigger=PropertyChanged}"
                                Header="???"
                                Width="100"
                                IsReadOnly="True"/>
        </DataGrid.Columns>
    </DataGrid>

For example : when i edit a row in datagrid i want to update the entire attributes , but in this case i can change Item, Count and City of a Order but weight doesnt update.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Since you want to display a calculated Weight value based on other properties, you should change the following parts of your code:

Make the binding one-way, since the value will always be re-computed in source.

<DataGridTextColumn Binding="{Binding Weight, Mode=OneWay}"
                    Header="???"
                    Width="100"
                    IsReadOnly="True"/>

Write the property as get-only calculation.

public int Weight
{
    get { return Item.Weight * Count; } // TODO: adjust if Item can be null
}

Notify for dependent changes in the source properties of your calculation. If Item.Weight could change within an item instance, you need additional handling.

private Item _Item;
public Item Item
{
    get { return _Item; }
    set
    {
        _Item = value;
        RaiseProperChanged();
        RaiseProperChanged(nameof(Weight));
    }
}
private int _Count;
public int Count
{
    get { return _Count; }
    set
    {
        _Count = value;
        RaiseProperChanged();
        RaiseProperChanged(nameof(Weight));
    }
}

Remove everything that accesses the Weight setter (for example in constructor).

See the following minimal working example for a calculated property. I rely on auto-generated columns in this case, but the same should be possible with hand written columns.

<Window
    ... your default generated window class, nothing special ... >
    <Grid x:Name="grid1">
        <DataGrid ItemsSource="{Binding}"/>
    </Grid>
</Window>

Viewmodel itemtype definition with dependent property Calculated:

public class ExampleItemViewModel : INotifyPropertyChanged
{
    private int _Number;
    public int Number
    {
        get { return _Number; }
        set
        {
            _Number = value;
            NotifyPropertyChanged();
            NotifyPropertyChanged("Calculated");
        }
    }

    public int Calculated { get { return 2 * Number; } }


    // INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged([CallerMemberName] string prop = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(prop));
    }
}

MainWindow constructor:

public MainWindow()
{
    InitializeComponent();

    var data = new List<ExampleItemViewModel>();
    data.Add(new ExampleItemViewModel { Number = 1 });
    data.Add(new ExampleItemViewModel { Number = 2 });

    grid1.DataContext = data;
}

What should happen: the DataGrid autogenerates an editable column for Number and a read-only column for Calculated. Since the columns are autogenerated, the default behavior applies: when you change a number, the source will not be updated immediately, because the row is in edit mode. It will be updated after the edit completes (eg. you press enter or the row loses focus). As soon as the source is updated, the dependent Calculated column value changes to 2 times the Number value.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...