-1

The end goal of my application is for a user to compare 2 DataTables. I have 2 DataGrids displayed side-by-side showing the DataTables, with the rows already rearranged so that any matching rows between the 2 tables are aligned.

My desire is that I want any non-matching rows to have a red background, like this: Yes I know this screenshot was made in WinForms

I have my XAML set up similar to this question:

<DataGrid Name="comparisonGridLeft" ItemsSource="{Binding}" Margin="3" CanUserResizeRows="False">
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Match}" Value="true">
                    <Setter Property="Background" Value="Red"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.RowStyle>
</DataGrid>

With my DependencyProperty "Match" defined similar to this answer:

public class CustomProperties
{
  public static DependencyProperty MatchProperty =
        DependencyProperty.RegisterAttached("Match",
                                            typeof(bool),
                                            typeof(CustomProperties),
                                            new PropertyMetadata(null));
  public static void SetMatch(DependencyObject obj, bool value)
  {
     obj.SetValue(MatchProperty, value);
  }
  public static bool GetMatch(DependencyObject obj)
  {
     return (bool)(obj.GetValue(MatchProperty));
  }
}

My final roadblock is that when I iterate through the DataGrids to set the "Match" property to the correct value, I get an error:

error CS1503: Argument 1: cannot convert from 'System.Data.DataRowView' to 'System.Windows.DependencyObject'

foreach (DataRowView leftRow in leftGrid.ItemsSource)
{
  foreach (DataRowView rightRow in rightGrid.ItemsSource)
  {
     bool foundMatch = DetermineIfMatch(leftRow, rightRow);
     if (foundMatch)
     {
        //Throws the compile-time error
        CustomProperties.SetMatch(leftRow, true);
        foundCloseMatch = true;
        break;
     }
  }
}

Thanks in advance for any help. New to WPF and have been working on this all day to no avail

Thomas Gales
  • 107
  • 2
  • 8

2 Answers2

0

You can't set an attached property on a DataRowView since as you see, it's not a DependencyObject which is a requirement for attached properties. What is the binding source for the DataGrids? If you control that object, you could stick the Match property in that object.

Just a side note... to me, your loop could just set the background color, there isn't really a need for an attached property.

EDIT: from the sound of things, I wouldn't get the data in a data table, but rather your own POCO:

class MyPoco
{
  string PropA {get;set}
  string PropB {get;set}
  Color Background {get;set}
}

Then have a List<MyPoco> and set the ItemsSource to that. Instead of auto binding, you'd have to define the columns yourself and bind those to the POCO properties:

<DataGrid ItemsSource={Binding Pocos}>
  <DataGrid.Columns>
    <DataGridTextColumn Binding="{Binding PropA}" />

Now you can add your styling property to the POCO, the Background property. Then you would define a DataGrid.RowStyle that sets the Background by binding to the Background property.

SledgeHammer
  • 7,338
  • 6
  • 41
  • 86
  • Thanks for your answer. I'm setting my binding source like this: comparisonGridLeft.ItemsSource = myDataTable.AsDataView(); The compiler is happy with me setting the Match property on myDataTable, but nothing appears to get updated. Do you know what kind of other changes I'd need to make for that to work? – Thomas Gales Dec 12 '18 at 23:22
  • Your side note seems much simpler- I'd need to have the DataGridRow instead of DataRowView though, right? I tried going that route but iterating through the DataGridRows seems to be tough/ugly to pull off – Thomas Gales Dec 12 '18 at 23:24
  • @ThomasGales myDataTable is an actual DataTable type? Because that isn't a DependencyObject either. Your code wouldn't build if you did that. What kind of object is it? – SledgeHammer Dec 12 '18 at 23:36
  • it's definitely an actual DataTable type so I'm not sure why it's compiling then. – Thomas Gales Dec 12 '18 at 23:37
  • @ThomasGales... if I try to write: DataTable dt = new DataTable(); CustomProperties.SetMatch(dt, true); it doesn't compile on the 'dt' not being a DependencyObject. Can you edit your question to show more of the code involved? – SledgeHammer Dec 12 '18 at 23:39
  • @ThomasGales but regardless... it doesn't matter... you wouldn't be able to use the DataTable to set DPs on the rows. – SledgeHammer Dec 12 '18 at 23:42
  • Ah, my bad. I was putting a DataGrid into SetMatch(), not a DataTable so that's why it compiled fine Also, for some reason "@SledgeHammer" isn't working for me.. – Thomas Gales Dec 12 '18 at 23:43
  • @ThomasGales That makes more sense :). You wouldn't be able to use the DataGrid to hold a AP since you need one for each row. How are you creating the DataTable? – SledgeHammer Dec 12 '18 at 23:49
  • I'm getting my DataTable from a SQLiteDataAdapter – Thomas Gales Dec 13 '18 at 22:46
0

You can't use the attached property in this case. This is how you can attack the problem

  1. Define cache, holding non matching datarows
static class CacheService
{
    public static readonly HashSet<DataRowView> Cache = new HashSet<DataRowView>();
}
  1. Do the comparison, build cache of differences
HashSet<DataRowView> _cache = CacheService.Cache;
foreach (DataRowView leftRow in leftGrid.ItemsSource)
{
  foreach (DataRowView rightRow in rightGrid.ItemsSource)
  {
      bool foundMatch = DetermineIfMatch(leftRow, rightRow);
      if (!foundMatch)
          _cache.Add(leftRow);
  }
}
  1. Tweak the XAML
<DataGrid Name="comparisonGridLeft" ItemsSource="{Binding}" Margin="3" CanUserResizeRows="False">
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="Background" Value="{Binding Path=., Converter={StaticResource MatchToBrushConverter}}" />
        </Style>
    </DataGrid.RowStyle>
</DataGrid>
  1. The converter
public class MatchToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        HashSet<DataRowView> _cache = CacheService.Cache; //Get the cache

        return _cache.Contains(value) ? Brushes.Red : Brushes.Transparent;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

FYI, I've not tested it.

Hope this helps.

Cinchoo
  • 6,088
  • 2
  • 19
  • 34
  • Thanks for your answer. Important to note for others looking at this answer that I needed to add my converters to Window.Resources as seen [here][1]. I was able to get it working with 2 Converters: one for the left table and one for the right table. [1]: http://wpftutorial.net/ValueConverters.html – Thomas Gales Dec 13 '18 at 16:35