x:Bind 事件绑定的语法给出错误 CS0122:由于保护级别而无法访问

x:Bind syntax for event binding giving error CS0122: inaccessible due to protection level

我在使用新绑定模式的 Windows 10 通用应用程序中有一个基本页面。

我正在将 ViewModel 加载到 MainPage.xaml.cs 代码隐藏的 public 属性 中。这个 ViewModel 包含一堆属性,我将它们绑定到我的控件上的属性,它们工作得很好。

public sealed partial class MainPage : Page
{
    public MainViewModel MainVM { get; set; }

    public MainPage()
    {
        this.MainVM = SimpleIoc.Default.GetInstance<MainViewModel>();
        this.InitializeComponent();
    }
... more stuff that isn't important ...
}

现在我想绑定到 ListView 上的 SelectionChanged 事件。我在我的 ViewModel 中使用以下内容:

public void AccountsSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    .... stuff ....
}

这是我的 XAML:

<ListView VerticalAlignment="Bottom"
          ItemsSource="{x:Bind MainVM.Accounts}"
          ItemTemplate="{StaticResource AccountItemTemplate}"
          SelectionMode="Single"
          SelectionChanged="{x:Bind MainVM.AccountsSelectionChanged}"
          Grid.Row="1">
    <ListView.Header>
        <TextBlock>Accounts</TextBlock>
    </ListView.Header>
</ListView>

这是疯狂的事情......有一段时间,这很奏效!几个小时后,我开始收到此错误:

Error CS0122 'MainPage.MainPage_obj1_Bindings.MainPage_obj1_BindingsTracking.cache_MainVM' is inaccessible due to its protection level

很可能是我在某处进行了更改导致了这种情况,但我不知道是在哪里。如果我删除对 SelectionChanged 事件的绑定,则此错误消失。但它以前是工作的!我不知道还能做什么。我已经尝试清理解决方案并在没有绑定的情况下重建,然后将其放回原处,但它不起作用。

我已经验证每个可能涉及的 class 都是 public 并且有一个 public 构造函数。重新启动 VS2015 RC 没有帮助,重新启动也没有帮助 Windows 10(内部版本 10074)。

EDIT - 当我将处理程序直接放在 MainPage.xaml.cs 的代码隐藏中时,我已经验证事件的 x:Bind 模式确实有效XAML:

SelectionChanged="{x:Bind AccountsSelectionChanged}"

我的视图模型是 public、public、public。我不确定我错过了什么。

任何人都可以提供其他东西来尝试吗?

这是一件很奇怪的事情。我的 MainPage 上有两个 ListView。首先,我有一个 ObservableCollection of Account classes 绑定为 ItemsSource。我还将 SelectionChanged 事件绑定到同一 ViewModel 上的处理程序。这是给我带来麻烦的事件。

在另一个 ListView 上,我想显示文件夹 classes 的 ObservableCollection,selected 帐户的 属性。

<ListView VerticalAlignment="Top"
    Margin="0,48,0,0"
    ItemsSource="{x:Bind MainVM.SelectedAccount.Folders, Mode=OneWay}"
    ItemTemplate="{StaticResource FolderItemTemplate}"
    Grid.Row="0">
    <ListView.Header>
         <TextBlock>Folders</TextBlock>
    </ListView.Header>
 </ListView>

SelectedAccount 在第一个 ListView 的事件处理程序中重新设置。 我在博客上读到 x:Bind 命令似乎默认为 OneTime 绑定而不是 OneWay,所以我在 ItemsSource 上将 Mode 设置为 OneWay。此绑定将要更改,因此我认为这是一个合适的设置。

然而,这就是我所有问题的根源!我 删除了 Mode=OneWay 设置 并且我的项目编译得很好。奇怪的是,我可以保留此 Mode=OneWay 设置并 删除 另一个 ListView 中的事件绑定,它会编译。我就是不能两者兼得。

我不知道这是否会影响我对这个 ListView 的计划,但至少我现在可以编译了。

EDIT - 这只是答案的一半......我无法使用这些约束进行正确绑定。我需要弄清楚为什么会这样。

编辑 2 我的解决方法是停止使用该事件,而是绑定到 ListView 上的 SelectedItem 属性。 SelectionMode 需要设置为 Single 才能正常工作。此外,SelectedItem 输出一个对象类型,因此我需要设置一个 getter/setter,在将对象传递给私有变量时将其转换为原始帐户 class。当我删除对事件的绑定时,我可以将此 ListView 属性 设置为 Mode=TwoWay 而不会出现任何问题。

编辑 3 实际上有两种解决方法。一般来说,这个总是有效的。只需在代码隐藏中处理事件。由于您的 ViewModel 是代码隐藏上的 public 属性,因此您可以从事件处理程序调用 ViewModel 上的 public 方法并输入相同的参数(或 none ).

//codebehind
//HeadersVM is my ViewModel  

private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        foreach (var item in e.AddedItems)
            HeadersVM.SelectedHeaders.Add((Models.MailHeader)item);
        foreach (var item in e.RemovedItems)
            HeadersVM.SelectedHeaders.Remove((Models.MailHeader)item);
    }

private void BackButton_Click(object sender, RoutedEventArgs e)
    {
        this.HeadersVM.GoBack();
    }

否则,对于特定于 ListView 的 SelectedItem 的解决方法,您可以双向绑定到 SelectedItem 属性。然后,创建一个转换器,其 ConvertBack 方法将对象转换为您的模型。

public class ObjectToAccountConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        if (value != null)
            return (Models.Account)value;
        return value;
    }
}

//in App.xaml Resources
<converters:ObjectToAccountConverter x:Key="ObjectToAccountConverter "/>

//in your Page.xaml
<ListView VerticalAlignment="Top"
    Margin="0,48,0,0"
    ItemsSource="{x:Bind MainVM.Accounts}"
    ItemTemplate="{StaticResource AccountTemplate}"
    SelectedItem="{x:Bind MainVM.SelectedAccount, Mode=TwoWay, Converter={StaticResouce ObjectToAccountConverter }}"
    Grid.Row="0">
    <ListView.Header>
         <TextBlock>Folders</TextBlock>
    </ListView.Header>
 </ListView>

OR 而不是创建转换器来将对象转换回您的模型,您可以将视图模型中的 属性 设为通用对象而不是您的实际对象class:

 private Models.Account _selectedAccount = null;

 public object SelectedAccount
 {
    get { return _selectedAccount; }
    set
    {
        if (_selectedAccount != value)
        {
           _selectedAccount = (Models.Account)value;
           RaisePropertyChanged();
           RaisePropertyChanged("Folders");
        }
     }
  }