简体   繁体   English

C#WPF DataGridTextColumn自定义属性

[英]C# WPF DataGridTextColumn custom property

I'm trying to bind data to some sort of custom dependency property or attached property in WPF. 我正在尝试将数据绑定到WPF中的某种自定义依赖项属性或附加属性。 I've used the following question and answer- How to create Custom Property for WPF DataGridTextColumn as a frame of reference but I need to bind to the 'tag' in WPF. 我已经使用以下问题和答案- 如何为WPF DataGridTextColumn创建自定义属性作为参考框架,但我需要绑定到WPF中的“标签”。 We can call it whatever, but I call it a tag for simplicity as I'm using it on some checkboxes and textboxes in other parts of the code. 我们可以使用任何名称,但为简单起见,我将其称为标记,因为我在代码其他部分的某些复选框和文本框上使用了它。 My Class is as follows: 我的班级如下:

public class TagTextColumns : DependencyObject
{
    public static readonly DependencyProperty TagProperty = DependencyProperty.RegisterAttached(
        "Tag",
        typeof(object),
        typeof(DataGridColumn),
        new FrameworkPropertyMetadata(null));

    public static object GetTag(DependencyObject dependencyObject)
    {
        return dependencyObject.GetValue(TagProperty);
    }

    public static void SetTag(DependencyObject dependencyObject, object value)
    {
        dependencyObject.SetValue(TagProperty, value);
    }
}

I'd like to setup my DataGridTextColumn in WPF to something similar to the following: 我想在WPF中将DataGridTextColumn设置为类似于以下内容:

<DataGridTextColumn Binding="{Binding Customer}" Header="Customer" local:TagTextColumns.Tag="{Binding tkey}"/>

where tkey is a reference in the db. 其中tkey是数据库中的引用。 I do all this to calculate subtotals in the last column per row. 我这样做是为了计算每行最后一列中的小计。 Right now, I use a DataGridTemplateColumn with a TextBox inside so I can Tag it with tkey I've also read up on TechNet about these things but there seems to be little pointing to what exactly I want to do here. 现在,我使用DataGridTemplateColumnTextBox里面,所以我可以Tag与TKEY它我也看到了关于这些事情的TechNet但似乎很少指向究竟我想在这里做。 I'm not even sure it's possible, but it would seem like it would be. 我什至不确定这是否可行,但似乎确实如此。

EDIT: 编辑:

This is the code I'm using to attempt to call the tag on CellEditEnding firing 这是我用来尝试在CellEditEnding触发时调用代码的代码

private void resultsDg_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
    {
        MessageBox.Show(TagTextColumns.GetTag(sender as DataGridTextColumn).ToString());
    }

More details about the problem at hand: I am trying on four fields from the database to dynamically calculate the subtotal for the last column as the user makes changes. 有关当前问题的更多详细信息:我正在尝试数据库中的四个字段,以便在用户进行更改时动态计算最后一列的小计。 I was trying to use Linq to SQL for this, but using System.Data.SqlClient and System.Data was much faster. 我试图为此使用Linq to SQL,但是使用System.Data.SqlClientSystem.Data的速度要快得多。 Maybe someone can show me a better way. 也许有人可以告诉我更好的方法。 The calculation code is below: 计算代码如下:

    private void CalculateAscessorials(string tkey)
    {
        decimal SplitTerm = Properties.Settings.Default.SplitTerminal;
        decimal SplitDrop = Properties.Settings.Default.SplitDrop;
        decimal SiteSplit = Properties.Settings.Default.OnSiteSplit;
        decimal Trainer = Properties.Settings.Default.TrainerLoad;
        decimal WaitTime = Properties.Settings.Default.TerminalWait;
        decimal PumpOut = Properties.Settings.Default.PumpOut;

        DataRow[] row = resultDetail.Select("tkey = " + tkey);

        decimal payRate;
        decimal tempLineItem;
        Decimal.TryParse(row[0]["PayForLine"].ToString(), out tempLineItem);
        Decimal.TryParse(row[0]["PayRate"].ToString(), out payRate);
        if (payRate == 0 && tempLineItem > 0) //this change affts if the rate is 0, that is no rate defined, the rate entered becomes the pay rate for that line only for calculations
            row[0]["PayRate"] = tempLineItem;
        if (!Convert.ToBoolean(row[0]["SplitDrop"]))
        {
            Decimal.TryParse(row[0]["PayRate"].ToString(), out payRate); 
        }
        else if (Convert.ToBoolean(row[0]["SplitDrop"]))
        {
            payRate = SplitDrop;
        }
        //decimal linePay;
        //    Decimal.TryParse(row[0]["PayForLine"].ToString(), out linePay);
        int terms;
        Int32.TryParse(row[0]["SplitLoad"].ToString(), out terms);
        decimal waits;
        Decimal.TryParse(row[0]["WaitTime"].ToString(), out waits);
        int pumps;
        Int32.TryParse(row[0]["PumpOut"].ToString(), out pumps);
        int sites;
        Int32.TryParse(row[0]["SiteSplit"].ToString(), out sites);
        int trainings;
        Int32.TryParse(row[0]["Trainer"].ToString(), out trainings);
        row[0]["PayForLine"] =
                    (SplitTerm * terms)
                    + (waits * WaitTime)
                    + (pumps * PumpOut)
                    + (sites * SiteSplit)
                    + (trainings * Trainer)
                    + payRate;
    }

You can use the MVVM pattern to calculate the subtotals in the ViewModel. 您可以使用MVVM模式在ViewModel中计算小计。 Here's a example that should lead you to the right direction. 这是一个示例,可以引导您正确的方向。 In the constructor of the MainView I create 2 courses and apply students. 在MainView的构造函数中,我创建2门课程并申请学生。 A course has a property TotalWeight which calculates the total weight of all students. 一门课程具有TotalWeight属性,该属性可以计算所有学生的总体重。 If the weight of a student changes, or a student is added/removed to the course we have to update the value on the UI. 如果学生的体重发生变化,或者添加/删除了学生,我们必须在UI上更新该值。 This due to INotifyPropertyChanged and ObservableCollection . 这是由于INotifyPropertyChangedObservableCollection所致。

If you use LinqToSQL or even better EntityFramework you can get from store. 如果您使用LinqToSQL或什至更好的EntityFramework,则可以从商店获取。 You could than inject it into the ViewModel constructor as the acutal model. 然后,您可以将其作为实际模型注入到ViewModel构造函数中。 Maybe you even want to introduce a MainViewModel for this purpose. 也许您甚至想要为此目的引入MainViewModel。 I skipped this for simplicity. 为了简单起见,我跳过了这个。 But you may want to have a look on dependency injection . 但是您可能想看看依赖注入

XAML: XAML:

    <DataGrid ItemsSource="{Binding}">
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid ItemsSource="{Binding Students}"></DataGrid>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>

code behind: 后面的代码:

public class CourseViewModel : ViewModelBase
{
    #region Fields

    private string _name;

    private ObservableCollection<StudentViewModel> _students;

    #endregion Fields

    #region Constructors

    public CourseViewModel()
    {
        _students = new ObservableCollection<StudentViewModel>();
        _students.CollectionChanged += _students_CollectionChanged;
    }

    #endregion Constructors

    #region Properties

    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public ObservableCollection<StudentViewModel> Students
    {
        get
        {
            return _students;
        }
    }

    public double TotalWeight
    {
        get
        {
            return Students.Sum(x => x.Weight);
        }
    }

    #endregion Properties

    #region Methods

    private void _students_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {

        // add/remove property changed handlers to students 
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            foreach (StudentViewModel student in e.NewItems)
            {
                student.PropertyChanged += Student_PropertyChanged;
            }
        }
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
        {
            foreach (StudentViewModel student in e.OldItems)
            {
                student.PropertyChanged -= Student_PropertyChanged;
            }
        }

        //students were added or removed to the course -> inform "listeners" that TotalWeight has changed
        OnPropertyChanged(nameof(TotalWeight));
    }

    private void Student_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //the weight of a student has changed -> inform "listeners" that TotalWeight has changed
        if (e.PropertyName == nameof(StudentViewModel.Weight))
        {
            OnPropertyChanged(nameof(TotalWeight));
        }
    }

    #endregion Methods
}

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{

    #region Constructors

    public MainWindow()
    {
        InitializeComponent();


        var course1 = new CourseViewModel() { Name = "Course1" };
        course1.Students.Add(new StudentViewModel() { Weight = 100, Name = "Mark" });
        course1.Students.Add(new StudentViewModel() { Weight = 120, Name = "Olaf" });
        course1.Students.Add(new StudentViewModel() { Weight = 111, Name = "Hans" });
        var course2 = new CourseViewModel() { Name = "Course2" };
        course2.Students.Add(new StudentViewModel() { Weight = 100, Name = "Mark" });
        course2.Students.Add(new StudentViewModel() { Weight = 90, Name = "Renate" });
        course2.Students.Add(new StudentViewModel() { Weight = 78, Name = "Judy" });

        DataContext = new List<CourseViewModel>()
        {
            course1,
            course2
        };
    }

    #endregion Constructors


}
public class StudentViewModel : ViewModelBase
{
    #region Fields

    private string _name;
    private double _weight;

    #endregion Fields

    #region Properties

    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public double Weight
    {
        get
        {
            return _weight;
        }
        set
        {
            _weight = value;
            OnPropertyChanged();
        }
    }

    #endregion Properties
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM