4

I need a way for viewmodel to instruct XamDataGrid on the view to just reread and repaint its cells with minimal hassle. I do not wish to mess with the source and do some unsustainable workaround with raising its events (the source might change).

To make it more understandable, I have a global static class that holds some visual cues configuration that do not affect data but only they way its represented in the grid (scaling, formatting, etc). The visual action happens in the IValueConverter implementation attached to the field which works just fine. There is a static event which fires when cues change and viewmodels subscribe to it and events fire properly. Now I just need to have the event handler cause the grid to repaint.

Any suggestions?

EDIT: some code, if it helps:

converter:

public class ScaleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (targetType == typeof(double) || targetType == typeof(double?))
        {
            if (value == null && targetType == typeof(double?)) return null;  // short circuit for null->null, nothing to scale
            if (value is double) return (double)value / DisplayScale.Scale; // shortcut for direct scaling, no need to waste time on conversion
            try
            {
                return System.Convert.ToDouble(value) / DisplayScale.Scale; // for convertible values, eat conversion exception
            }
            catch (Exception) {};
            // if all esle fails return NaN
            return double.NaN; 
        }
        // fallthrough, return null, this should not happen, if it does it means converter is incomplete
        return null;
    }
 ...
}

DisplayScale global

public class DisplayScale: NotificationObject
{
    private static KeyValuePair<string, double> _ActiveScaling;
    private static readonly object _lockHandle = new object(); // note: should not contest, but just to be on the safe side
    public static event Action ScalingChanged;

    public static List<KeyValuePair<string, double>> ScaleList { get; private set; }

    static DisplayScale()
    {
        ScaleList = new List<KeyValuePair<string, double>>() 
        { 
            new KeyValuePair<string, double>("No scaling (1's)", 1d),
            new KeyValuePair<string, double>("Thousands (1,000's)", 1000d),
            new KeyValuePair<string, double>("Millions (1,000,000's)", 1000000d),
            new KeyValuePair<string, double>("Billions (1,000,000,000's)", 1000000000d)
         };
        ActiveScaling = ScaleList.First();  // OPTION: could be in per-user config
    }

    public static double Scale { get { return ActiveScaling.Value; } }
    public static KeyValuePair<string, double> ActiveScaling
    {
        get
        {
            lock (_lockHandle) return _ActiveScaling;
        }
        set
        {
            lock (_lockHandle)
            {
                _ActiveScaling = value;
                var eventCall = ScalingChanged;
                if (eventCall != null) eventCall();
            }
        }
    }
}

fields are defined as

// resource
<inf:ScaleConverter x:Key="ScaleConverter" />
// field
<igDP:Field Name="Deposit" Converter="{StaticResource ScaleConverter}">
mmix
  • 6,057
  • 3
  • 39
  • 65
  • can you post some code? do you have cellvaluepresenter style(s)? – punker76 Dec 05 '11 at 12:04
  • I can post the code, but it's irrelevant, I just want the grid to do whatever it is doing when source raises ListChanged event without actually having to resort to raising ListChanged (because in reality it has not changed, what has changed are the instructions for value converters). – mmix Dec 05 '11 at 12:52
  • Have you implemented the INotifyPropertyChanged interface for your dataclass? The "Deposit" property must fire the PropertyChanged event to trigger your ValueConverter. if you have a CollectionView than call simply Refresh() to it. – punker76 Dec 05 '11 at 13:08
  • data source implements `INotifyPropertyChange` and when the DATA changes it refreshes as it should. The problem is I need it to do that as well when the PRESENTATION SETTING changes (DisplayScale.Scale). I cannot find a way to do that. The `ScalingChanged` event fires properly and if I tie viewmodel to it it receives notification but I cant find a way to cause grid to refresh when data has not changed. – mmix Dec 05 '11 at 14:15

2 Answers2

4

if you have a collectionview than call simply Refresh() to it.

public class YourViewModel
{
  private ObservableCollection<YourDataClass> yourColl;

  public void YourViewModel()
  {
    yourColl = new ObservableCollection<YourDataClass>();
    YourCollectionView = CollectionViewSource.GetDefaultView(yourColl);
    DisplayScale.ScalingChanged += () => YourCollectionView.Refresh();
  }

  var ICollectionView yourCollView;
  public ICollectionView YourCollectionView
  {
    get { yourCollView; }
    set {
      yourCollView = value;
      RaisePropertyChanged("YourCollectionView");
    }
  }
}
punker76
  • 14,326
  • 5
  • 58
  • 96
  • Hmm, this might actually work, provided that Refresh() does not alert other observers of the data source. I'll try it out. Thanks – mmix Dec 05 '11 at 14:22
0

I was having the same issue, and it turns out even though I had my ViewModel implement INotifyPropertyChanged, I was not implementing INotifyPropertyChanged on the class that the grid rows were binding to in my ObservableCollection (i.e. the YourDataClass in punker76's answer). Once I implemented it on that class as well everything started updating as expected.

I know that you mentioned "I do not wish to mess with the source" so I'm not sure if this solution is acceptable for you, but I thought I would share for others that find this post as well.

deadlydog
  • 22,611
  • 14
  • 112
  • 118