简体   繁体   中英

C# WPF DataGridTextColumn custom property

I'm trying to bind data to some sort of custom dependency property or attached property in 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. 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:

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

where tkey is a reference in the db. 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. 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

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. 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. 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. A course has a property TotalWeight which calculates the total weight of all students. 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. This due to INotifyPropertyChanged and ObservableCollection .

If you use LinqToSQL or even better EntityFramework you can get from store. You could than inject it into the ViewModel constructor as the acutal model. Maybe you even want to introduce a MainViewModel for this purpose. I skipped this for simplicity. But you may want to have a look on dependency injection .

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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