【问题标题】:MVVM Binding Issue - Another Noob-ish IssueMVVM 绑定问题 - 另一个菜鸟问题
【发布时间】:2013-02-05 20:25:29
【问题描述】:

所以我又来了,问very similar question to yesterday。我重构了我的项目,以便更好地遵循 MVVM 模式。现在我的绑定不再像昨天那样工作了。我正在尝试将停靠面板的可见性绑定到按钮。这是我的一些代码:

视图模型:

public class SelectWaferButtonViewModel : INotifyPropertyChanged
{
    private bool isClicked;

    public SelectWaferButtonViewModel()
    {
        isClicked = false;
    }

    public bool IsControlVisible
    {
        get
        {
            return isClicked;
        }
        set
        {
            isClicked = value;
            OnPropertyChanged("IsControlVisible");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnButtonClick()
    {
        if (isClicked)
        {
            IsControlVisible = false;
        }
        else
        {
            IsControlVisible = true;
        }
    }
    protected virtual void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}

XAML:

<Window.Resources>
     <local:BoolToVisibilityConverter x:Key="BoolToVisConverter"/>
     <local:SelectWaferButtonViewModel x:Key="SelectWaferButton" />
     <local:WaferTrackerWindowViewModel x:Key="WindowViewModel" />
</Window.Resources>
<DockPanel
     Name="tvwDockPanel"
     DataContext="{StaticResource SelectWaferButton}"
     Width="225"
     Visibility="{Binding IsControlVisible, Mode=TwoWay, 
                  FallbackValue=Collapsed, 
                  Converter={StaticResource BoolToVisConverter}}"
     DockPanel.Dock="Left">
 </DockPanel>

我的BoolToVisConverter

public class BoolToVisibilityConverter : IValueConverter
{
    public BoolToVisibilityConverter() { }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool bValue = (bool) value;
        if (bValue)
        {
            return Visibility.Visible;
        }
        else
        {
            return Visibility.Collapsed;
        }
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Visibility visibility = (Visibility) value;
        if (visibility == Visibility.Visible)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

对于一个与昨天类似的问题,我深表歉意,但由于我对 WPF 很陌生,因此我正在努力解决这个 MVVM 问题。任何帮助将不胜感激。

  • 非常感谢,

编辑: 这里有一些额外的代码 sn-ps 供进一步参考:

public class WaferTrackerWindowViewModel :INotifyPropertyChanged
{
    private SelectWaferButtonViewModel btnSelectWaferViewModel;
    public event PropertyChangedEventHandler PropertyChanged;
    private DelegateCommand exitCommand;
    private DelegateCommand expandPanelCommand;
    private DelegateCommand selectWaferCommand;

    public WaferTrackerWindowViewModel()
    {
        this.InstantiateObjects();
        initThread.RunWorkerAsync();
    }

    public string SelectedWafer
    {
        get
        {
            return selectedWafer;
        }
        set
        {
            selectedWafer = value;
        }
    }
    public ICommand ExitCommand
    {
        get
        {
            if (exitCommand == null)
            {
                exitCommand = new DelegateCommand(Exit);
            }
            return exitCommand;
        }
    }
    public ICommand ExpandPanelCommand
    {
        get
        {
            if (expandPanelCommand == null)
            {
                expandPanelCommand = new DelegateCommand(ExpandPanel);
            }
            return expandPanelCommand;
        }
    }
    public ICommand SelectWaferCommand
    {
        get
        {
            if (selectWaferCommand == null)
            {
                selectWaferCommand = new DelegateCommand(SelectWafer);
            }
            return selectWaferCommand;
        }
    }

    private void InstantiateObjects()
    {
        btnSelectWaferViewModel = new SelectWaferButtonViewModel();
        initThread = new BackgroundWorker();
    }
    private void ExpandPanel()
    {
        btnSelectWaferViewModel.OnButtonClick();
    }
    private void SelectWafer()
    {
       //Does Nothing Yet
    }
    private void Exit()
    {
        Application.Current.Shutdown();
    }
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private void InitThread_DoWork(object sender, DoWorkEventArgs e)
    {
        TreeViewPresenter tvwPresenter = new TreeViewPresenter();
        tvwPresenter.WaferList = DataLibrary.GetWaferList();
    }
    private void InitThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        tvwPresenter.TreeView.DataContext = tvwPresenter.ProcessesAndWafers;
        tvwPresenter.WaferListCache = tvwPresenter.ProcessesAndWafers;
        tvwPresenter.ProcessArray = tvwPresenter.WaferListCache.ToArray();
    }
}

当点击“展开面板”按钮时,它会调用ExpandPanel 命令,该命令将执行路由到同一类中的“private void ExpandPanel()”方法。然后,在ExpandPanel() 方法中,它调用btnSelectWaferViewModel 对象上的OnButtonClick() 方法,这将更改IsControlVisible 属性。然后,此更改应反映到绑定的停靠面板上,但这不会发生

凯尔

【问题讨论】:

  • 您知道 WPF 中已经存在BooleanToVisibilityConverter 吗?
  • 由于他自己定义了属性,他可以将其设为可见性,而不是将其存储为布尔值。
  • @sircodesalot:在我看来,将Visbility 放入您的 ViewModel 会混合过多的 View 逻辑。我认为IsVisible 属性可以更好地捕捉意图。这也是 OP 昨天接受的答案的意见,所以你建议他们改回来真的让这个问题感到困惑!
  • 您可以将点击处理程序简化为 IsControlVisible = !isClicked 而不是 if/else 块。
  • 请显示与如何调用OnButtonClick 相关的代码。

标签: c# .net wpf data-binding mvvm


【解决方案1】:

(1) ViewModel 应该在Window.DataContext 部分,而不是Window.Resources 部分。

(2) 在您的视图模型中,将您的IsControlVisible 属性设为System.Windows.Visibility,而不是布尔值,那么您就不需要转换器了。

(3) 我看不到OnButtonClick 有任何触发方式,确实需要设置ICommand 接口。

(4) 您不需要实现ConvertBack,因为您要绑定的Visibility 属性根据定义是一种方式。用户无法设置visibilityfalse

(5) 不要混合访问IsClicked 和它的访问者IsControlVisible。始终在 MVVM 中使用访问器,因为您冒着意外设置 IsClicked 的风险,这不会激活 OnPropertyChanged

总而言之,你已经很接近了。请务必留意您的“输出”窗口,它会告诉您绑定是否由于某种原因失败。但是,是的,坚持住!

【讨论】:

  • 1.) 我没有使用 WindowViewModel 来绑定这个控件,我正在为这个按钮使用一个特定的 ViewModel。根据我的经验,我只能在 Window.DataContext 属性中设置一个数据上下文。
  • 2.) 昨天使用布尔类型和 bool-to-vis 转换器对我有用,所以我认为这不是问题
  • 评论无关。取消。
  • 3.) OnButtonClick 由于我定义的 ICommand 接口而触发。我没有发布这部分,因为我知道这个 ICommand 接口确实将按钮单击路由到这个方法。然后,此方法更改 ViewModel 上的“IsControlVisible”属性的值。当我单步执行代码时,我可以看到所有这一切。由于某种原因,当 IsControlVisible 属性发生更改时,停靠面板的 Visibility 属性不会更改,所以我认为问题出在绑定
  • 您可以尝试其他几件事。 (1) 检查输出窗口,它经常会告诉你绑定是否失败。或者 (2) 您可以在转换器中使用 Debug.Writeline,因为(假设属性是绑定的)您可以找出传递给转换器的内容。
【解决方案2】:

所以当你这样做时:

<Window.Resources>
    <local:SelectWaferButtonViewModel x:Key="SelectWaferButton" />
</Window.Resources>

WPF 将创建SelectWaferButtonViewModel 的新实例并将其添加到它的资源中。然后,您可以通过使用带有键的StaticResource 设置DataContext 来绑定到它。

但是,如果您在后面的代码中创建另一个 SelectWaferButtonViewModel 并将您的命令链接到该实例,则它不是同一个实例,因此更改此未绑定实例的属性不会影响您的 UI。有几种方法可以解决它。您可以 a) 在后面的代码中创建单个 SelectWaferButtonViewModel 作为属性,然后在 XAML 中绑定到该属性,或者 b) 在 XAML 中声明您当前拥有的 SelectWaferButtonViewModel,然后在后面的代码中检索该实例,像这样:

SelectWaferButtonViewModel swbvm = (SelectWaferButtonViewModel)this.FindResource("SelectWaferButton");

编辑:因此,在看到您的最后一次编辑后,如果您想使用 a),那么我建议您将 btnSelectWaferViewModel 公开为您的 WaferTrackerWindowViewModel 中的一个属性,然后绑定到该属性将 Window 的 DataContext 设置为 WaferTrackerWindowViewModel 实例。所以你最终会得到类似的东西:

<DockPanel
    Name="tvwDockPanel"
    Width="225"
    Visibility="{Binding MyButton.IsControlVisible, 
        Converter={StaticResource BoolToVisConverter}}"
    DockPanel.Dock="Left">
</DockPanel>

和:

public class WaferTrackerWindowViewModel :INotifyPropertyChanged
{
    private SelectWaferButtonViewModel btnSelectWaferViewModel;
    public SelectWaferButtonViewModel MyButton 
    {
        get { return btnSelectWaferViewModel; }
        set
        {
            btnSelectWaferViewModel = value;
            OnPropertyChanged("MyButton");
        }
    }
    //......

【讨论】:

  • 我试过这个,我发现当这个方法被调用时: private void ExpandPanel() { btnSelectWaferViewModel.OnButtonClick(); btnSelecteWaferViewModel 对象为空,这很奇怪。问题是,在初始化之后,当命令触发时,它使用的 WaferTrackerWindowViewModel 对象与后面代码中设置的 datacontext 对象不同。我不知道为什么会这样,但是在命令触发后我中断了,我看到 WaferTrackerWindowViewModel 类中的所有内容都设置为 null
猜你喜欢
  • 1970-01-01
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-26
  • 1970-01-01
相关资源
最近更新 更多