【问题标题】:WPF UserControl: Invoke usercontrol's public method from viewModelWPF UserControl:从 viewModel 调用 usercontrol 的公共方法
【发布时间】:2017-07-20 13:23:54
【问题描述】:

我在 C#、NET 3.5 和 Visual Studio 2008 中有一个 MVVM WPF 应用程序。

我从应用程序主 xaml 导入用户控件。

这个用户控件有一些公共方法,有两个我比较感兴趣。

一种启动动画的方法和另一种停止动画的方法。

从代码隐藏 (xaml.cs) 中的视图构造函数中,我调用用户控件公共方法来启动动画以将其显示给用户,同时我将一些数据加载到列表视图中的网格视图中。加载数据的方法在我的视图模型中调用。

所以现在,当加载任务完成时,我需要调用另一个用户控件公共方法来停止动画,但我不知道如何从我的视图模型中执行此操作。

有什么想法吗?我无法触摸用户控件,因为这不是我的。

下面是一段代码。

XAML

xmlns:controlProgress="clr-namespace:Common.XAML.Controls.Progress;assembly=Common.XAML"

<controlProgress:Progress x:Name="Progress"  
                        Grid.ZIndex="3" 
                        HorizontalAlignment="Center" 
                        VerticalAlignment="Center" 
                        Width="150"  
                        CustomText="Loading..."> 

代码隐藏 (xaml.cs):

    public MyView(ViewModelSession vm)
        : base(vm)
    {            
        InitializeComponent();

        Progress.StartAnimation();
    }

查看模型:

    public MyViewModel(Session session)
        : base(session)
    {            
        this.LoadDataIntoGridView(); 
    }

【问题讨论】:

  • “我无法触摸用户控件,因为这不是我的。”那么你就不走运了,因为视图模型没有对视图的引用,所以它不能直接调用它的任何方法。
  • 我刚刚发布了我的答案,看到那条邪恶的线...我立即删除了它..
  • 把你不能碰的UC包在你能碰的UC里。然后将您的 VM 绑定到外部 UC。您可以从外部 UC 的代码隐藏对 VM 中的状态更改做出反应,并对封装的 UC 执行任何您想要的操作。显示它,隐藏它,调用它的方法,打它的脸,等等。

标签: c# wpf xaml mvvm user-controls


【解决方案1】:

您可以使用INotifyPropertyChanged 接口,例如创建一个 ViewModelBase

public class ViewModelBase
    : INotifyPropertyChanged
{      
    public event PropertyChangedEventHandler PropertyChanged;  

    private void OnPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

然后你将它用于你的 ViewModel 并添加一个 Property IsLoading

 public class MyViewModel : ViewModelBase
{            
    private bool _isLoading;

    public bool IsLoading
    {
      get { return _isLoading; }
      set
      {
        if(_isLoading == value) return;
         _isLoading = value;
         OnPropertyChanged();
      }
}

然后在您的 View Codebehind 中使用 ViewModel 的 PropertyChanged 事件来启动/停止动画。

然后您可以在 ViewModel 中设置 bool 以开始停止关闭动画 在你看来

更新

public class MyView
{
    private readonly MyViewModel _viewModel;

    public MyView(MyViewModel viewModel)
          : base(viewModel)
    {
        InitializeComponent();
        _viewModel = viewModel;
        _viewModel.PropertyChanged +=OnPropertyChanged;
    }

    private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(MyViewModel.IsLoading))
        {
            if (_viewModel.IsLoading)
            {
                Progress.StartAnimation();
            }
            else
            {
                Progress.StopAnimation();
            }
        }
    }
}

【讨论】:

  • 我认为他的意思是他不能触摸 controlProgress 用户控件
  • 这可能是有道理的,在这种情况下是可能的。然而 new MyViewModel().PropertyChanged +=OnPropertyChanged; 不正确,但 _viewModel.PropertyChanged +=OnPropertyChanged;
  • @Mainpat 是的,我的意思是我无法触摸 controlProgress 用户控件,您的解决方案对我来说很好。谢谢。只有一件事,OnPropertyChanged 方法,在 .NET 3.5 中,不支持 nameof ;)
【解决方案2】:

您可以在视图模型中放置一个布尔属性来跟踪加载是否已完成,之后该属性将设置为 true。

 public class MyViewModel
    {

        public bool IsLoadComplete { get; set; }
        public MyViewModel()
        {
            this.LoadDataIntoGridView();
        }
    }

然后在您的代码隐藏中,您可以启动一个任务来跟踪 DataContext 的该属性的更改:

 public MyView(MyViewModel vm)
        {
            InitializeComponent();

            Progress.StartAnimation();

            Task.Run(() =>
            {
                var dataContext = DataContext as MyViewModel;
                while (true)
                {
                    if (dataContext.IsLoadComplete)
                        break;
                    Task.Delay(100);
                }
                Dispatcher.BeginInvoke(new Action(() => { Progress.StopAnimation(); }));
            });
        }

您必须使用 Dispatcher.BeginInvoke 在 UI 线程中对调用进行排队。当然,这不是一个现成的解决方案。您可以提供 Datacontext 直到 View 被构建,在这种情况下您必须重构,您也可以跟踪您刚刚开始的任务,并且可能支持使用 CancellationToken 取消。这只是一个示例

【讨论】:

    猜你喜欢
    • 2013-11-12
    • 2019-09-05
    • 1970-01-01
    • 2021-07-26
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    相关资源
    最近更新 更多