X: Binding syntax for event binding giving error CS0122: not available due to security level

I have a basic page in a Windows 10 Universal App that I'm using a new binding template from.

I am loading the ViewModel to a public property in the MainPage.xaml.cs code. This ViewModel contains a bunch of properties that I bind to properties on my controls and they work fine.

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 ...
}

      

Now I want to bind the SelectionChanged event on the ListView. I am using the following in my ViewModel:

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

      

And this is in my 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>

      

Here's a crazy thing ... for a while, it worked! After a few hours, I started getting this error:

Error CS0122 "MainPage.MainPage_obj1_Bindings.MainPage_obj1_BindingsTracking.cache_MainVM" is not available due to protection level

It is very possible that I changed something that caused this condition, but I have no idea where. If I remove the binding to the SelectionChanged event, this error goes away. But it was JOB! I don't know what else to do. I tried cleaning the solution and rebuilding without linking and then pasting it back in and it doesn't work.

I have verified that all possible classes are public and have a public constructor. Restarting VS2015 RC did not help and did not restart Windows 10 (Build 10074).

EDIT - I've verified that the x: Bind template for events works when I put the handler directly into the code on MainPage.xaml.cs using this XAML:

SelectionChanged="{x:Bind AccountsSelectionChanged}"

      

My viewmodel is public, public, public. I'm not sure what I'm missing.

Can anyone provide something else to try?

+3


source to share


1 answer


It was kind of strange. I have two ListViews on my MainPage. First, I have an ObservableCollection of Account class bound as an ItemSource. I also have a SelectionChanged event associated with a handler on the same ViewModel. This event gave me problems.

In another ListView, I want to display the ObservableCollection of Folder class, property of the selected account.

<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>

      

The SelectedAccount is returned to the event handler from the first ListView. I read on a blog that the x: Bind command was bound to OneTime by default, not OneWay, so I set Mode to OneWay on ItemsSource. This commitment has changed, so I assumed it was a suitable setting.

However, this is what was causing all my problems! I removed the Mode = OneWay setting and my project compiled just fine. Oddly enough, I can exit in this Mode = OneWay and remove the event binding in another ListView and compile it. I just can't have both.

I don't know if it will affect my plans for this ListView, but at least I can compile it now.

EDIT . This is only half of the answer ... I cannot properly attach to these constraints. I need to figure out why this is happening.



EDIT 2 My workaround is to stop using this event and instead bind to the SelectedItem property on the ListView. SelectionMode must be set to Single to work properly. Also, SelectedItem infers the type of the object, so I need to set up a getter / setter that converts the object to the original Account class when passing it to a private variable. I can set this ListView property to Mode = TwoWay without any problem when I remove the event binding.

EDIT 3 There are actually two workarounds. In general, this one always works. Just handle the event in codeb. Since your ViewModel is a public property in your code, you can call the public method on the ViewModel from an event handler and put in the same parameters (or 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();
    }

      

Otherwise, for a ListView specific SelectedItem workaround, you can simply bind the TwoWay property to the SelectedItem property. Then create a converter, the ConvertBack method turns the object into your model.

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 instead of creating a converter to convert the object back to your model, you can simply make the property in your view model a generic object instead of your actual class:

 private Models.Account _selectedAccount = null;

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

      

0


source







All Articles