How to call method in window (.xaml.cs) from viewmodel (.cs) without introducing new references in wpf

后端 未结 2 2041
闹比i
闹比i 2021-01-01 18:12

I\'m looking for a simple way to call a method in my Main Window, but I want to call it from my View Model. Basically, I\'m looking for some king of \"this.parent\" sort of

相关标签:
2条回答
  • 2021-01-01 18:41

    first of all, it's not a stupid question. Most of MVVM starters came from winforms and it's normal to have the tendency to bring in your winforms practices and work on code behind. Now all you have to do is forget that and think MVVM.

    Going back to your question, you have a dictionary that your VM is processing and you are accessing that dictionary from the view. Your view should not have any idea about your viewmodel except through binding.

    Making a window flash when there are changes in the viewmodel sounds like an attached behavior to me. Here's a good read about attached behavior. http://www.codeproject.com/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF

    To make it easier, I'll try to give you a very simple example that will somehow be relevant to your case.

    Create an attached behavior class where you have an IEnumerable where in whenever you add something a messagebox will appear on the screen. Just change the messagebox code to whatever flashing animation you would like to do on notify.

    public class FlashNotificationBehavior
    {
        public static readonly DependencyProperty FlashNotificationsProperty =
            DependencyProperty.RegisterAttached(
            "FlashNotifications",
            typeof(IEnumerable),
            typeof(FlashNotificationBehavior),
            new UIPropertyMetadata(null, OnFlashNotificationsChange));
    
        private static void OnFlashNotificationsChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var collection = e.NewValue as INotifyCollectionChanged;
    
            collection.CollectionChanged += (sender, args) => 
                {
                    if (args.Action == NotifyCollectionChangedAction.Add)
                    {
                        foreach (var items in args.NewItems)
                            MessageBox.Show(items.ToString());
                    }
                };            
        }
    
        public static IEnumerable GetFlashNotifications(DependencyObject d)
        {
            return (IEnumerable)d.GetValue(FlashNotificationsProperty);
        }
    
        public static void SetFlashNotifications(DependencyObject d, IEnumerable value)
        {
            d.SetValue(FlashNotificationsProperty, value);
        }
    }
    

    In your viewmodel, you can create an ObservableCollection property, you need an observable collection so there is a collection changed event notification. I also added a command for adding so that you can test it.

       public class MainViewModel : ViewModelBase
    {
        ObservableCollection<string> notifications;
    
        public ObservableCollection<string> Notifications
        {
            get { return notifications; }
            set
            {
                if (notifications != value)
                {
                    notifications = value;
                    base.RaisePropertyChanged(() => this.Notifications);
                }
            }
        }
    
        public ICommand AddCommand
        {
            get
            {
                return new RelayCommand(() => this.Notifications.Add("Hello World"));
            }
        }
    
        public MainViewModel()
        {
            this.Notifications = new ObservableCollection<string>();             
        }
    }
    

    And here's a view where you can bind it the Notifications proeprty from your view model.

    <Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:WpfApplication7.ViewModel"
        xmlns:local="clr-namespace:WpfApplication7"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <vm:MainViewModel />
    </Window.DataContext>
    <Grid>
        <StackPanel>
            <ListBox ItemsSource="{Binding Notifications}" 
                     local:FlashNotificationBehavior.FlashNotifications="{Binding Notifications}"></ListBox>
            <Button Command="{Binding AddCommand}" >Add Something</Button>
        </StackPanel>
    </Grid>
    

    Everytime you add something in the ObservableCollection, you will get a messagebox notifying the user that something has been added to your collection.

    I hope that I helped in your problem. Just tell me if you need some clarifications.

    0 讨论(0)
  • 2021-01-01 18:42

    VM should "know" nothing of your View or Window, the way VM typically "communicates" with V in WPF/MVVM is by rasing events. That way VM remains ignorant of/decoupled from the V and since VM is already DataContext of V it's not hard to subscribe to VM's event.

    Example:

    VM:

    public event EventHandler<NotificationEventArgs<string>> DoSomething;
    ...
    Notify(DoSomething, new NotificationEventArgs<string>("Message"));
    

    V:

    var vm = DataContext as SomeViewModel; //Get VM from view's DataContext
    if (vm == null) return; //Check if conversion succeeded
    vm.DoSomething += DoSomething; // Subscribe to event
    
    private void DoSomething(object sender, NotificationEventArgs<string> e)
    {
        // Code    
    }
    
    0 讨论(0)
提交回复
热议问题