How to handle Series MouseDown in OxyPLot.WPF in MVVM way?

I am using OxyPlot to draw some diagrams in my .NET WPF project. MVVM is a new thing to me, so i am not sure how to follow it in this situation.

  1. There is a window (main view) that contains PlotView.
  2. There is a model that contains a collection of objects. Each object corresponds to a graph on my plot.
  3. There is a view model which creates LineSeries, puts it into PlotModel.

The problem is that i want each graph to be clickable. After a click another window opens with more detailed information on the corresponding object. My current view model is something like this:

public class MainViewModel : BaseViewModel
{
    public PlotModel MyPlotModel { get; set; }

    private MyModel model;


    public MainViewModel()
    {
        model = new MyModel();
        DrawGraphs();
    }


    private void DrawGraphs()
    {
        List<MyObject> myObjects = model.GetObjects();

        foreach (var obj in myObjects)
        {
            var s = new LineSeries();
    
            // Setting up datapoints and appearance of the graph

            s.MouseDown += (object sender, OxyMouseDownEventArgs e) =>
            {
                var view = new ObjectView(); // A view with full information of this specific object
                view.DataContext = new ObjectViewModel(obj); // View model uses the object as a parameter to do bindings for new view
                view.ShowDialog();
            };
            MyPlotModel.Series.Add(s);
        }

        OnPropertyChanged("MyPlotModel");
    }
}

MouseDown can be set up only on series creation because i need to know a specific object from my model that corresponds to this graph.

It works fine to me, however, i doubt that implementation follows MVVM. View model should not be able to create new views. But i cannot come up with a better solution. Is there a more elegant and MVVM-ish way to do this?

4

With a few caveats, this is a more MVVM-friendly approach (I say more because it may not be feasible to do a truly pure MVVM approach to the extent I understand this library). In short, you would move some of the view-opening functionality into a view-level class to manage your views and bind it to an ObservableCollection on the view model, to which you can add ObjectViewModel instances that will trigger the creation of new views:

Static ObjectViewManager Class with Attached Property

public static class ObjectViewManager
{
    public static readonly DependencyProperty ViewsProperty =
        DependencyProperty.RegisterAttached(
            "Views",
            typeof(INotifyCollectionChanged),
            typeof(ObjectViewManager),
            new PropertyMetadata(OnViewsChanged));
    public static INotifyCollectionChanged GetViews(DependencyObject obj)
    {
        return (INotifyCollectionChanged)obj.GetValue(ViewsProperty);
    }
    public static void SetViews(DependencyObject obj, INotifyCollectionChanged value)
    {
        obj.SetValue(ViewsProperty, value);
    }
    private static void OnViewsChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        if (e.OldValue is INotifyCollectionChanged oldIncc)
            oldIncc.CollectionChanged -= OnViewsCollectionChanged;
        if (e.NewValue is INotifyCollectionChanged newIncc)
            newIncc.CollectionChanged += OnViewsCollectionChanged;
    }

    private static void OnViewsCollectionChanged(
        object? sender,
        NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                if (e.NewItems == null)
                    return;
                foreach (var item in e.NewItems)
                {
                    var view = new ObjectView(); // A view with full information of this specific object
                    view.DataContext = item; // View model uses the object as a parameter to do bindings for new view
                    view.ShowDialog();
                    if (sender is IList list)
                    {
                        // Optionally remove the view model
                        // from the collection when the dialog
                        // closes so view model and view remain
                        // in sync
                        list.Remove(item);
                    }
                }
                break;
        }
    }
}

Changes to View Model

public class MainViewModel : BaseViewModel
{
    // ...

    ObservableCollection<ObjectViewModel> _ObjectViewModels;
    public ObservableCollection<ObjectViewModel> ObjectViewModels
    {
        get
        {
            return _ObjectViewModels ?? 
                (_ObjectViewModels = 
                    new ObservableCollection<ObjectViewModel>());
        }
    }

    private void DrawGraphs()
    {
        List<MyObject> myObjects = model.GetObjects();

        foreach (var obj in myObjects)
        {
            var s = new LineSeries();

            // *** More about this event handler below ***
            s.MouseDown += (object sender, OxyMouseDownEventArgs e) =>
            {
                // Rather than create `ObjectView` here, 
                // adding it to the ObservableCollection 
                // will trigger invocation of the collection change
                // handler above, provided the ObjectViewModels
                // property is bound somewhere in XAML
                this.ObjectViewModels.Add(new ObjectViewModel(obj));
            };
            MyPlotModel.Series.Add(s);
        }

        OnPropertyChanged("MyPlotModel");
    }
}

Somewhere in XAML, on some root view or even MainWindow

  <SomeRootView local:ObjectViewManager.Views="{Binding ObjectViewModels}" />

That’s more or less it, but now for a pretty big chunk of salt:

  • The MouseDown handler in the view model is an MVVM no-no, albeit a qualified one in this case. It’s qualified because as far as I can tell (though I’m not familiar with the library) LineSeries doesn’t look like it inherits from a WPF type. So they’ve already given you, it seems, an abstraction layer between the underlying UI and your application logic, which one could argue makes it ok for the view model because it doesn’t tether you to one UI platform. However, purists would tell you that’s still no excuse, because the very concept of MouseDown is too much in the UI implementation detail camp to be worthy of view model treatment.

  • The problem then is where to put it? It just may not be possible to put it anywhere else. Moreover, the library and any code that relies on it is already platform-independent. If you changed it around to create the LineSeries in the view layer, just so you can handle the MouseDown event in the “correct” place, then all you’re doing is making more of your code WPF platform dependent. Why bother?

OxyPlot is clearly a highly cross-platform library. These can be tricky to work with in a pure MVVM fashion unless specifically designed to be MVVM friendly (e.g., MAUI) because many times they’ve already done cross-platform in their own style, and making them MVVM friendly would just tether them more closely to WPF, defeating the very purpose.

If that’s the case, then I say don’t fight the current. By all means pull in MVVM techniques to the extent they help you maintain cross-platform compatibility (which my example hopefully does), but otherwise just embrace their flavor of doing things. After all, there’s no reason to use any design pattern unless it brings more benefit than cost.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật