2013-08-07 12 views
7

Mam problem z określonym wiązaniem danych XAML. Mam dwa listboxes (master-details, więc listboxes mają wartość IsSynchronizedWithCurrentItem na true). Chcę, aby mój ViewModel, aby wiedzieć, kiedy wybrana pozycja na zmianach dane pola listy: Stworzyłem właściwość int na mojej klasie ViewModel (czyli możemy wywołać tę właściwość SelInd) oraz szczegóły ViewModel wiążę ten sposób:SelectedIndex z wiązaniem OneWayToSource nie wyzwala

SelectedIndex="{Binding Mode=OneWayToSource, Path=SelInd}" 

Nie otrzymuję błędów/wyjątków w czasie wykonywania, ale powiązanie nie wyzwala: moja właściwość viewmodel nie jest aktualizowana po zmianie wybranego elementu. Jeśli zmienię tryb bindowania na TwoWay, wszystko działa dobrze, ale to nie jest to, czego potrzebuję. Potrzebuję go do pracy z OneWayToSource (btw to samo niestosowne zachowanie ma zastosowanie, jeśli wiążę właściwości SelectedItem do SelectedValue).

Dlaczego te wiązania nie działają z OneWayToSource?

Oto bardziej kompletny przykład kodu, aby uzyskać wyraźniejsze efekty: EDYCJA: Nie mogę wyświetlić prawdziwego kodu (NDA), ale pokażę tutaj coś prostszego i wystarczająco podobnego (Kontekst danych strony jest instancją opisanej później klasy PageViewModel) Po prostu potrzebuję, aby moja właściwość SelInd klasy viewmodel zawsze odzwierciedlała wartość SelectedIndex w drugim polu ListBox. Znalazłem alternatywne metody robienia tego (Event handler with code-behind lub Attached Behavior), ale teraz jestem po prostu ciekawy dlaczego nie działa z wiązaniem OneWayToSource.

<Page> 
    <ContentControl x:Name="MainDataContext"> 
     <Grid DataContext={Binding Path=Masters}> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="*" /> 
       <ColumnDefinition Width="*" /> 

      </Grid.ColumnDefinitions> 

      <ListBox Grid.Column="0" 
       SelectionMode="Single"       
      IsSynchronizedWithCurrentItem="True" 
      ItemsSource="{Binding }"> 
       <ListBox.ItemContainerStyle> 
      ... 
      </ListBox.ItemContainerStyle> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
       .... 
        </DataTemplate> 
      </ListBox.ItemTemplate> 
      </ListBox> 

      <ListBox Grid.Column="1" 
       SelectionMode="Single"       
       SelectedIndex="{Binding Mode=OneWayToSource, ElementName=MainDataContext,Path=DataContext.SelInd}" 
      IsSynchronizedWithCurrentItem="True" 
      ItemsSource="{Binding Path=Details}"> 
       <ListBox.ItemContainerStyle> 
      ... 
      </ListBox.ItemContainerStyle> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
       .... 
        </DataTemplate> 
      </ListBox.ItemTemplate> 
      </ListBox> 
     </Grid> 
    </ContentControl> 
</Page> 

Oto szkic klasy widok modelu

public class PageViewModel{ 
    public ObservableCollection<MasterClass> Masters {get;set;} 

    public int SelInd {get;set;} 

    .... 
} 

A oto MasterClass, to po prostu posiada nazwę i listę szczegółów

public class MasterClass{ 
    public ObservableCollection<DetailsClass> Details {get;set;} 

    public String MasterName {get;set;} 

    .... 
} 
+1

try (updateSourceTriger = PropertyChanged) Część w swojej wiążące wiedzieć ..hope masz już zaimplementowane inotifypropertyChnahged..on you viewmodel .. – loop

+0

dzięki tanuj_loop. Zapomniałem wspomnieć o tym w pytaniu: już próbowałem używać updateSourceTrigger, ale nic się nie zmieniło. W każdym razie mój viewmodel już implementuje INotifyPropertyChanged, ale myślę, że jest to przydatne tylko wtedy, gdy musisz powiadomić UI o zmianach viewmodelu, a nie odwrotnie. – Gufino2

+0

co to jest MainDataContext? zaktualizuj kod viewmodel i pełny kod xaml ... i co właściwie chcesz osiągnąć .. – loop

Odpowiedz

4

myślę, że w Twoim przypadku, musi korzystać z trybu OneWay. Domyślnie używany jest tryb TwoWay.

Cytat MSDN o TwoWay:

TwoWay wiązania powoduje zmiany zarówno właściwości źródłowego lub docelowego obiektu, aby automatycznie aktualizować inne. Ten typ wiązania jest odpowiedni dla formularzy do edycji lub innych w pełni interaktywnych scenariuszy interfejsu użytkownika. Większość właściwości jest domyślnie powiązana z OneWay, ale niektóre właściwości zależności (zazwyczaj właściwości edytowalnych przez użytkownika kontrolek, takich jak właściwość Text w TextBox i właściwość IsChecked CheckBox) domyślnie wiążą się z TwoWay. Programistycznym sposobem określenia, czy właściwość zależności domyślnie wiąże obiekt jednokierunkowy, czy dwukierunkowy, jest uzyskanie metadanych właściwości właściwości za pomocą funkcji GetMetadata, a następnie sprawdzenie wartości Boolean właściwości BindsTwoWayByDefault.

Tryb OneWay, że need:

OneWay wiązania powoduje zmiany właściwości źródłowego automatycznie zaktualizować właściwość bramkę, jednak zmiany właściwości target nie są propagowane z powrotem do właściwości source . Ten rodzaj powiązania jest odpowiedni, jeśli kontrolowane wiązanie jest niejawnie tylko do odczytu. Na przykład możesz powiązać źródło takie jak giełdowy giełdowy lub być może twoja własność docelowa nie ma interfejsu kontrolnego przewidzianego do wprowadzania zmian, takiego jak kolor tła tabeli związany z danymi.Jeśli nie ma potrzeby monitorowania zmian właściwości docelowej, użycie trybu wiązania OneWay pozwala uniknąć narzutu w trybie wiązania TwoWay.

Tryb OneWayToSource:

OneWayToSource jest reverse wiązania OneWay; aktualizuje właściwość źródłową, gdy właściwość docelowa ulegnie zmianie. Jednym z przykładów jest sytuacja, w której konieczna jest ponowna ocena wartości źródłowej z interfejsu użytkownika.

Poniżej jest schemat dla lepszego zrozumienia:

enter image description here

porządku, potem pokażę wam przykład, który pracuje dla mnie. Być może przyda ci się to.

XAML

<Window x:Class="SelectedIndexHelp.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:SelectedIndexHelp" 
    Title="MainWindow" Height="350" Width="525" 
    ContentRendered="Window_ContentRendered" 
    WindowStartupLocation="CenterScreen"> 

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

    <Grid DataContext="{StaticResource SelectedIndexClass}"> 
     <ListBox x:Name="MyListBox" 
       BorderThickness="1" 
       Width="200" Height="200" 
       BorderBrush="#CE5E48" 
       DisplayMemberPath="Name" 
       Background="AliceBlue" 
       SelectedIndex="{Binding MySelectedIndex, Mode=OneWayToSource}" /> 

     <Label Name="SelectedIndex" VerticalAlignment="Top" 
       Content="{Binding MySelectedIndex}" 
       ContentStringFormat="SelectedIndex: {0}" 
       Width="100" Height="30" Background="Lavender" /> 
    </Grid> 
</Window> 

Code behind

public partial class MainWindow : Window 
{ 
    public class Person 
    { 
     public string Name 
     { 
      get; 
      set; 
     } 

     public int Age 
     { 
      get; 
      set; 
     }    
    } 

    private ObservableCollection<Person> DataForListBox = new ObservableCollection<Person>(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void Window_ContentRendered(object sender, EventArgs e) 
    { 
     DataForListBox.Add(new Person() 
     { 
      Name = "Sam", 
      Age = 22, 
     }); 

     DataForListBox.Add(new Person() 
     { 
      Name = "Nick", 
      Age = 21, 
     }); 

     DataForListBox.Add(new Person() 
     { 
      Name = "Cris", 
      Age = 25, 
     }); 

     DataForListBox.Add(new Person() 
     { 
      Name = "Josh", 
      Age = 36, 
     }); 

     DataForListBox.Add(new Person() 
     { 
      Name = "Max", 
      Age = 32, 
     }); 

     DataForListBox.Add(new Person() 
     { 
      Name = "John", 
      Age = 40, 
     }); 

     MyListBox.ItemsSource = DataForListBox; 
     MyListBox.Focus(); 
    } 
} 

public class SelectedIndexClass 
{ 
    private int? mySelectedIndex = 0; 

    public int? MySelectedIndex 
    { 
     get 
     { 
      return mySelectedIndex; 
     } 

     set 
     { 
      mySelectedIndex = value; 
     } 
    } 
} 

Output

enter image description here

W tym przykładzie, istnieje klasa danych - Person te dane dla ListBox. I klasa (), która zawiera właściwość MySelectedIndex, która jest parametrem wiązania OneWayToSource.

Edit: Cieszę się, że zrozumiałeś problem. Spróbuję wyjaśnić na ich przykładzie, dlaczego nie pracujesz z przypadkiem ElementName.

Więc powiedzmy, że mamy ten kod:

<ContentControl x:Name="MainDataContext"> 
    <Grid x:Name="MainGrid" DataContext="{StaticResource SelectedIndexClass}"> 
     <ListBox x:Name="MyListBox" 
        BorderThickness="1" 
        Width="200" Height="200" 
        BorderBrush="#CE5E48" 
        DisplayMemberPath="Name" 
        Background="AliceBlue" 
        SelectedIndex="{Binding Path=DataContext.MySelectedIndex, Mode=OneWayToSource, ElementName=MainDataContext}" /> 

     <Label Name="SelectedIndex" VerticalAlignment="Top" 
       Content="{Binding MySelectedIndex}" 
       ContentStringFormat="SelectedIndex: {0}" 
       Width="100" Height="30" Background="Lavender" /> 
    </Grid> 
</ContentControl> 

Jak zapewne zrozumieć, to nie będzie działać.

DataContext Ustaw na określonym węźle drzewa wizualnego, wszystkie elementy poniżej (w drzewie wizualnym) dziedziczą go. Oznacza to, że DataContext będzie working od Grid i poniżej drzewa wizualnego. Dlatego poniższy kod zadziała:

<ContentControl x:Name="MainDataContext"> 
    <Grid x:Name="MainGrid" DataContext="{StaticResource SelectedIndexClass}"> 
     <ListBox x:Name="MyListBox" 
        BorderThickness="1" 
        Width="200" Height="200" 
        BorderBrush="#CE5E48" 
        DisplayMemberPath="Name" 
        Background="AliceBlue" 
        SelectedIndex="{Binding Path=DataContext.MySelectedIndex, Mode=OneWayToSource, ElementName=MainGrid}" /> 

     <Label Name="SelectedIndex" VerticalAlignment="Top" 
       Content="{Binding MySelectedIndex}" 
       ContentStringFormat="SelectedIndex: {0}" 
       Width="100" Height="30" Background="Lavender" /> 
    </Grid> 
</ContentControl> 

A także, to będzie działać, jeśli nazwa punktu MyListBox. Zwykle po ustawieniu DataContext nazwa elementu jest przekazywana.

+0

Dziękuję za wyjaśnienia, ale potrzebuję zachowania OneWayToSource: chcę, aby mój listbox zaktualizował właściwość w moim viewmodelu, a nie w inny sposób około :) – Gufino2

+0

@ Gufino2: Przepraszam, nie rozumiem poprawnie. Czy możesz pokazać pełny kod swojej klasy za pomocą zaimplementowanej właściwości? –

+0

Niestety, nie mogę pokazać prawdziwego kodu, ale właściwość jest tylko standardową właściwością C#, na przykład: public int myProp {get; set;} Po prostu chcę, aby moja wiążąca wartość TARGET (właściwość SelectedIndex w kontrolce ListBox) aktualizowała wartość mojego powiązania SOURCE (regularna właściwość C# w mojej klasie viewmodel). Po prostu potrzebuję, aby moja klasa vm wiedziała, kiedy użytkownik wybierze element z listy, ale użycie zaimplementowanej obsługi kodu lub dołączonego zachowania wydaje się być brudne i przesadne, gdy istnieje tryb bindowania, który POWINNO działać :) – Gufino2

2

Cóż, znalazłem sposób, aby to zadziałało. Właśnie usunąłem "kontekst" kontekstu danych, więc nie muszę używać elementu ElementName w moich powiązaniach i zaczęło działać.Grupa Robocza XAML przykładem jest:

<Page> 
    <ContentControl > 
     <Grid > 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="*" /> 
       <ColumnDefinition Width="*" /> 

      </Grid.ColumnDefinitions> 

      <ListBox Grid.Column="0" 
       SelectionMode="Single"       
      IsSynchronizedWithCurrentItem="True" 
      ItemsSource="{Binding Masters }"> 
       <ListBox.ItemContainerStyle> 
      ... 
      </ListBox.ItemContainerStyle> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
       .... 
        </DataTemplate> 
      </ListBox.ItemTemplate> 
      </ListBox> 

      <ListBox Grid.Column="1" 
       SelectionMode="Single"       
       SelectedIndex="{Binding Mode=OneWayToSource, Path=SelInd}" 
      IsSynchronizedWithCurrentItem="True" 
      ItemsSource="{Binding Path=Masters/Details}"> 
       <ListBox.ItemContainerStyle> 
      ... 
      </ListBox.ItemContainerStyle> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
       .... 
        </DataTemplate> 
      </ListBox.ItemTemplate> 
      </ListBox> 
     </Grid> 
    </ContentControl> 
</Page> 

Teraz, jeśli ktoś wie dokładnie, dlaczego wiązanie za pomocą ElementName nie działa, chciałbym go :)

+0

Proszę zobaczyć moją edycję. Próbowałem opisać twoją sprawę i dlaczego to nie zadziałało. –

Powiązane problemy