【问题标题】:Do compiled bindings (x:Bind) require the ViewModel to derive from DependencyObject?编译的绑定 (x:Bind) 是否需要 ViewModel 从 DependencyObject 派生?
【发布时间】:2019-01-23 15:32:54
【问题描述】:

我正在尝试在使用 Catel MVVM 框架实现的项目上使用编译绑定(应该没关系)。然而,在生成的代码中,我得到了编译器错误,因为我的 ViewModel 没有实现通常由 DependencyObjects 公开的某些函数:

CS1061“MainViewModel”不包含“RegisterPropertyChangedCallback”的定义,并且找不到接受“MainViewModel”类型的第一个参数的扩展方法“RegisterPropertyChangedCallback”(您是否缺少 using 指令或程序集引用?)

还有:

CS0039 无法通过引用转换、装箱转换、拆箱转换、包装转换或空类型转换将类型“Windows.UI.Xaml.DependencyObject”转换为“CatelCompiledBindingTest.ViewModels.MainViewModel”

x:Bind 编译器生成的引发错误的代码如下:

public void DependencyPropertyChanged_VM_Visible(global::Windows.UI.Xaml.DependencyObject sender, global::Windows.UI.Xaml.DependencyProperty prop)
{
    MainView_obj1_Bindings bindings = TryGetBindingObject();
    if (bindings != null)
    {
        global::CatelCompiledBindingTest.ViewModels.MainViewModel obj = sender as global::CatelCompiledBindingTest.ViewModels.MainViewModel;
        if (obj != null)
        {
            bindings.Update_VM_Visible(obj.Visible, DATA_CHANGED);
        }
    }
}
private global::CatelCompiledBindingTest.ViewModels.MainViewModel cache_VM = null;
private long tokenDPC_VM_Visible = 0;
public void UpdateChildListeners_VM(global::CatelCompiledBindingTest.ViewModels.MainViewModel obj)
{
    if (obj != cache_VM)
    {
        if (cache_VM != null)
        {
            ((global::System.ComponentModel.INotifyPropertyChanged)cache_VM).PropertyChanged -= PropertyChanged_VM;
            cache_VM.UnregisterPropertyChangedCallback(global::CatelCompiledBindingTest.ViewModels.MainViewModel.VisibleProperty, tokenDPC_VM_Visible);
            cache_VM = null;
        }
        if (obj != null)
        {
            cache_VM = obj;
            ((global::System.ComponentModel.INotifyPropertyChanged)obj).PropertyChanged += PropertyChanged_VM;
            tokenDPC_VM_Visible = obj.RegisterPropertyChangedCallback(global::CatelCompiledBindingTest.ViewModels.MainViewModel.VisibleProperty, DependencyPropertyChanged_VM_Visible);
        }
    }
}

MainViewModel 相当简单(不包括 Catel 样板代码):

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        ToggleVisibility = new Command(OnToggleVisibilityExecute);
        AddText = new Command(OnAddTextExecute);
        SomeText = "";
    }

    //ViewModel properties

    public string SomeText
    {
        get { return GetValue<string>(SometextProperty); }
        set { SetValue(SometextProperty, value); }
    }

    public bool Visible
    {
        get { return GetValue<bool>(VisibleProperty); }
        set { SetValue(VisibleProperty, value); }
    }

    //ViewModel Commands

    public Command ToggleVisibility { get; private set; }

    public void OnToggleVisibilityExecute()
    {
        Visible = !Visible;
    }

    public Command AddText { get; private set; }

    public void OnAddTextExecute()
    {
        SomeText += "Random text! ";
    }
}

视图如下:

<controls:Page
    x:Class="CatelCompiledBindingTest.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CatelCompiledBindingTest.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:controls="using:Catel.Windows.Controls"
    xmlns:converters="using:Catel.MVVM.Converters"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Page.Resources>
        <converters:BooleanToCollapsingVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    </Page.Resources>

    <Grid>
        <Grid.KeyboardAccelerators>
            <KeyboardAccelerator Invoked="{x:Bind VM.OnToggleVisibilityExecute}" Key="T" Modifiers="Control"/>
            <KeyboardAccelerator Invoked="{x:Bind VM.OnAddTextExecute}" Key="Add" Modifiers="Control"/>
        </Grid.KeyboardAccelerators>

        <Border Width="500" Height="500" Visibility="{x:Bind VM.Visible, Mode=OneWay}" Background="SlateGray">
            <TextBlock Style="{ThemeResource HeaderTextBlockStyle}" Text="{x:Bind VM.SomeText, Mode=OneWay}"/>
        </Border>

    </Grid>
</controls:Page>

通过以下代码隐藏能够使用已编译的绑定:

public sealed partial class MainView : Page
{
    public MainView()
    {
        this.InitializeComponent();
        VM = DataContext as MainViewModel;
    }

    public MainViewModel VM { get; set; }
}

现在的问题是:为什么编译器在编译器生成的DependencyPropertyChanged_VM_Visible 中假定 MainViewModel 的类型为DependencyObject?根据Data Binding in Depth,ViewModel 只需要实现IPropertyChanged就可以了。

我该如何解决这个问题(除了求助于{Binding}again)?我需要在 ViewModel 中包含哪些命名空间才能使编译的 Bindings 工作?

【问题讨论】:

  • 如果您的项目处于初始阶段,我建议您使用“Windows Template Studio”来生成您的 uwp 项目,它确实为您处理各种 biolerplate,包括许多流行的 MVVM 框架,您可以专注于您的业​​务逻辑,它的视图模型类还附带了用于双向绑定的可观察内容,看看:github.com/Microsoft/WindowsTemplateStudio
  • 我已经尝试过了,但是已经从 TemplateStudio/MVVMLight 更改为 Catel,因为 Catel 的功能更加丰富——尤其是在嵌套用户控件方面。
  • 那么希望 catel 专家可以帮助您。似乎 catel 使用了不同的绑定方式

标签: c# xaml mvvm uwp catel


【解决方案1】:

它假定它是一个依赖属性,因为VM 属性是在视图MainView 上声明的。您应该将 VM 属性定义为依赖属性,以便它可以绑定到(所以不是属性的值/类型,而是属性本身)。

【讨论】:

  • 嗨,Geert,感谢您的回复。这也是我首先假设的事情之一,但是将 VM 声明为依赖属性并没有帮助。在使用 MVVMLight 且代码几乎完全相同的参考项目中,即使 VM 刚刚声明为常规属性,该方法也能正常工作。比较项目之间的 MainView.g.cs 显示错误行不是由参考项目中的编译器创建的。 Catel 是否以不同的方式处理绑定?哪个工具生成 g.cs?微软构建?我还没有找到任何开源的东西来看看它在寻找什么......
  • 你能在 GitHub 上提供一个 repro 以便我们研究吗?
  • 我在这里创建了一个存储库:github.com/TradeItEasy/Compiled-Bindings 非常感谢您研究它!
  • 嗨,Geert,您有时间调查这个问题吗?你能重现这个问题还是让我很笨?
猜你喜欢
  • 1970-01-01
  • 2015-12-11
  • 1970-01-01
  • 2015-08-19
  • 2020-09-29
  • 2020-08-13
  • 1970-01-01
  • 2021-11-22
相关资源
最近更新 更多