2009-06-16 14 views
6

Mam aplikację, w której wyświetlam UserControls w GroupBox. Aby wyświetlić formanty, wiążę właściwość w ViewModel głównego formularza, który zwraca ViewModel do wyświetlenia. Mam DataTemplates skonfigurowane tak, aby formularz automatycznie wiedział, które UserControl/View użyć do wyświetlenia każdego ViewModel.Widok WPF Ustawia właściwości ViewModel na wartość null przy zamykaniu

Kiedy wyświetlić inny UserControl, trzymam ViewModel poprzedniej kontroli aktywny, ale widoki są usuwane automatycznie przez WPF.

Problem, który mam, polega na tym, że gdy widok zostanie zamknięty, wszelkie dwukierunkowe powiązania z właściwościami w ViewModelu są natychmiast ustawiane na wartość null, a gdy ponownie wyświetlam ViewModel, wszystkie wartości są ustawione do wartości null w interfejsie użytkownika.

Zakładam, że jest to spowodowane tym, że jako część zamknięcia widoku, zawiera i kasuje wszelkie wartości w kontrolkach, które zawiera, a ponieważ powiązania są na miejscu, propagują również do modelu ViewModel.

DataTemplates w moich zasobach

<DataTemplate DataType="{x:Type vm:HomeViewModel}"> 
    <vw:HomeView /> 
</DataTemplate> 
<DataTemplate DataType="{x:Type vm:SettingsViewModel}"> 
    <vw:SettingsView /> 
</DataTemplate> 
<DataTemplate DataType="{x:Type vm:JobListViewModel}"> 
    <vw:JobListView /> 
</DataTemplate> 

kod używany do wyświetlania użytkownik kontroluje

<GroupBox> 
    <ContentControl Content="{Binding Path=RightPanel}" /> 
</GroupBox> 

przykładem kontrolą że jestem wiązania w jednym z widoków:

<ComboBox Name="SupervisorDropDown" ItemsSource="{Binding Path=Supervisors}" DisplayMemberPath="sgSupervisor" 
      SelectedValuePath="idSupervisor" SelectedValue="{Binding Path=SelectedSupervisorID}" /> 

i odpowiednie właściwości ViewModel:

public ObservableCollection<SupervisorsEntity> Supervisors 
    { 
     get 
     { 
      return supervisors; 
     } 
    } 

public int? SelectedSupervisorID 
{ 
    get 
    { 
     return selectedSupervisorID; 
    } 
    set 
    { 
     selectedSupervisorID = value; 
     this.OnPropertyChanged("SelectedSupervisorID"); 
    } 
} 

Każdy pomysł, w jaki sposób zatrzymać moich poglądów Zerowanie wartości w moich ViewModels? Myślę, że być może trzeba ustawić DataContext widoku null, zanim zostanie ona zamknięta, ale nie jestem pewien, jak to zrobić z tym, jak rzeczy są obecnie wiążące.

Odpowiedz

0

Znalazłem jedno możliwe rozwiązanie, ale naprawdę tego nie lubię.

Okazuje się, że DataContext IS ma już ustawioną wartość null, ale to nie pomaga. Dzieje się tak, zanim właściwość zostanie ustawiona na wartość null. Wygląda na to, że powiązania danych nie są usuwane zanim UserControl/View pozbędzie się samego siebie, a zatem wartość pusta rozchodzi się w dół po usunięciu kontroli.

Więc gdy zmienia się DataContext, jeśli nowy kontekst jest null następnie usunąć odpowiednich powiązań na ComboBox, co następuje:

private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 
{ 
    if (e.NewValue == null) 
    { 
     SupervisorDropDown.ClearValue(ComboBox.SelectedValueProperty); 
    } 
} 

Nie jestem wielkim fanem tej metody, ponieważ oznacza to, Muszę to zrobić, pamiętaj, aby zrobić to dla każdego używanego kontrolera danych. Gdyby był sposób, mógłbym po prostu kazać każdej UserControl po prostu usunąć swoje powiązania automatycznie, gdy zamkną, że byłoby ok, ale nie mogę wymyślić żadnego sposobu, aby to zrobić.

Innym rozwiązaniem może być po prostu restrukturyzacji moją aplikację, tak że poglądy nie dostać zniszczone aż ViewModels zrobić - to byłoby ominąć ten problem całkowicie.

+0

Znam ten sam problem. Ustawienie DataContext dzieci wizualnych na wartość null częściowo rozwiązało problem. Ukrywanie widoku zamiast go niszczyć nie robiło różnicy. Wciąż szukam kompletnego rozwiązania. – HappyNomad

0

Kiedy wyświetlić inny UserControl, trzymam ViewModel z poprzednia kontrola aktywny, ale Widoki są usuwane automatycznie przez WPF.

Problem, który mam jest, że gdy widok zamyka dwa jakikolwiek sposób Wiązania do właściwości w ViewModel są natychmiast ustawiona na null, a więc kiedy wyświetli ViewModel znowu wszystko wartości są po prostu ustawione na na wartość null w interfejsie użytkownika.

Nie jestem ekspertem w żadnej z WPF ani MVVM, ale coś w tym nie brzmi dobrze. Mam problem z przekonaniem, że usuwanie WPF widoku powoduje problem. Przynajmniej w moim ograniczonym doświadczeniu nigdy nie miałem czegoś podobnego. Podejrzewam, że winowajcą jest albo kod w modelu widoku, albo kod zmieniający, który model widoku jest używany dla datacontext.

0

Po próbie zatrzymania ustawienia zerowego różnymi sposobami poddałem się i zamiast tego zadziałałem w następujący sposób. Zrobiłem ViewModel tylko do odczytu przed zamknięciem jego widoku. Osiągam to w mojej klasie ViewModelBase, gdzie dodałem właściwość boolean IsReadOnly. Następnie w ViewModelBase.SetProperty() (patrz poniżej) ignoruję wszelkie zmiany właściwości, gdy IsReadOnly ma wartość true.

protected bool SetProperty<T>(ref T backingField, T value, string propertyName) 
    { 
     var change = !IsReadOnly && !EqualityComparer<T>.Default.Equals(backingField, value); 

     if (change) { 
      backingField = value; 
      OnPropertyChanged(propertyName); 
     } 
     return change; 
    } 

Wygląda na to, że działa tak, chociaż nadal chciałbym poznać lepsze rozwiązanie.

0

Miałem ten sam problem. Co pracowało dla mnie było usunięcie UpdateSourceTrigger = PropertyChanged z moich SelectedValueBindings. PropertyChanged UpdateSourceTriggers wydają się ognia na właściwości związanych z zamknięciem widoki podczas korzystania z tego wzoru:

<!--Users DataGrid--> 
<DataGrid Grid.Row="0" ItemsSource="{Binding DealsUsersViewSource.View}" 
    AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="False" 
    HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
    <DataGrid.Resources> 
     <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="#FFC5D6FB"/> 
    </DataGrid.Resources> 
    <DataGrid.Columns> 

      <!--Username Column--> 
      <DataGridComboBoxColumn 
      SelectedValueBinding="{Binding Username}" Header="Username" Width="*"> 
       <DataGridComboBoxColumn.ElementStyle> 
        <Style TargetType="{x:Type ComboBox}"> 
         <Setter Property="ItemsSource" Value="{Binding DataContext.DealsUsersCollection.ViewModels, 
          RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" /> 
         <Setter Property="SelectedValuePath" Value="Username"/> 
         <Setter Property="DisplayMemberPath" Value="Username"/> 
        </Style> 
       </DataGridComboBoxColumn.ElementStyle> 
       <DataGridComboBoxColumn.EditingElementStyle> 
        <Style TargetType="{x:Type ComboBox}"> 
         <Setter Property="ItemsSource" Value="{Binding DataContext.BpcsUsers, 
          RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" /> 
         <Setter Property="SelectedValuePath" Value="Description"/> 
         <Setter Property="DisplayMemberPath" Value="Description"/> 
         <Setter Property="IsEditable" Value="True"/> 
        </Style> 
       </DataGridComboBoxColumn.EditingElementStyle> 
      </DataGridComboBoxColumn> 

      <!--Supervisor Column--> 
      <DataGridComboBoxColumn 
      SelectedValueBinding="{Binding Supervisor}" Header="Supervisor" Width="*"> 
       <DataGridComboBoxColumn.ElementStyle> 
        <Style TargetType="{x:Type ComboBox}"> 
         <Setter Property="ItemsSource" Value="{Binding DataContext.DealsUsersCollection.ViewModels, 
          RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" /> 
         <Setter Property="SelectedValuePath" Value="Username"/> 
         <Setter Property="DisplayMemberPath" Value="Username"/> 
        </Style> 
       </DataGridComboBoxColumn.ElementStyle> 
       <DataGridComboBoxColumn.EditingElementStyle> 
        <Style TargetType="{x:Type ComboBox}"> 
         <Setter Property="ItemsSource" Value="{Binding DataContext.BpcsUsers, 
          RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" /> 
         <Setter Property="SelectedValuePath" Value="Description"/> 
         <Setter Property="DisplayMemberPath" Value="Description"/> 
         <Setter Property="IsEditable" Value="True"/> 
        </Style> 
       </DataGridComboBoxColumn.EditingElementStyle> 
      </DataGridComboBoxColumn> 

      <!--Plan Moderator Column--> 
      <DataGridCheckBoxColumn Binding="{Binding IsPlanModerator}" Header="Plan Moderator?" Width="*"/> 

      <!--Planner Column--> 
      <DataGridCheckBoxColumn Binding="{Binding IsPlanner}" Header="Planner?" Width="*"/> 

    </DataGrid.Columns> 
</DataGrid> 

Pojemnik Widok:

<!--Pre-defined custom styles--> 
<a:BaseView.Resources> 

    <DataTemplate DataType="{x:Type vm:WelcomeTabViewModel}"> 
     <uc:WelcomeTabView/> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type vm:UserSecurityViewModel}"> 
     <uc:UserSecurityView/> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type vm:PackItemRegisterViewModel}"> 
     <uc:PackItemsRegisterView/> 
    </DataTemplate> 

</a:BaseView.Resources> 

<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="30"/> 
     <ColumnDefinition Width="*"/> 
     <ColumnDefinition Width="30"/> 
    </Grid.ColumnDefinitions> 

    <Grid.RowDefinitions> 
     <RowDefinition Height="30"/> 
     <RowDefinition Height="*"/> 
     <RowDefinition Height="30"/> 
    </Grid.RowDefinitions> 

    <TabPanel Grid.Column="1" Grid.Row="1"> 
     <TabControl TabStripPlacement="Top" ItemsSource="{Binding TabCollection}" SelectedIndex="{Binding SelectedTabIndex}" 
        DisplayMemberPath="DisplayName" MinWidth="640" MinHeight="480"/> 
    </TabPanel> 

</Grid> 

Pojemnik ViewModel:

TabCollection.Add(new WelcomeTabViewModel()); 
TabCollection.Add(new UserSecurityViewModel(_userService, _bpcsUsersLookup)); 
TabCollection.Add(new PackItemRegisterViewModel(_packItemService, _itemClassLookup)); 
SelectedTabIndex = 0; 
0

Ustaw UpdateSourceTrigger wyraźny do LostFocus

Jeśli widok jest zamknięcie i ustawia swoje dane do null, to nie ma wpływu na dane w viewmodel.

<ComboBox Name="SupervisorDropDown" ItemsSource="{Binding Path=Supervisors}" DisplayMemberPath="sgSupervisor" 
SelectedValuePath="idSupervisor" 
SelectedValue="{Binding Path=SelectedSupervisorID, UpdateSourceTrigger=LostFocus}" /> 
Powiązane problemy