2009-06-16 21 views
6

Tengo una aplicación en la que estoy mostrando UserControls en un GroupBox. Para mostrar los controles, estoy vinculando a una propiedad en el modelo de vista del formulario principal, que devuelve un modelo de vista para mostrar. Tengo DataTemplates configurado para que el formulario sepa automáticamente qué UserControl/View usar para mostrar cada ViewModel.WPF View establece las propiedades de ViewModel para anular el cierre

Cuando visualizo un UserControl diferente, mantengo el ViewModel del control anterior activo, pero las Vistas son descartadas automáticamente por WPF.

El problema que tengo es que cuando la vista se apaga, cualquier enlace bidireccional a las propiedades en el modelo de vista se establece inmediatamente en nulo, por lo que cuando visualizo ViewModel nuevamente, todos los valores se han establecido anular en la interfaz de usuario.

Supongo que esto se debe a que, como parte del cierre de la vista, elimina y borra cualquier valor en los controles que contiene, y como los enlaces están en su lugar, también se propagan al ViewModel.

DataTemplates en mis recursos

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

código utilizado para mostrar los controles del usuario

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

Ejemplo de un control que estoy de unión en una de las Vistas:

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

y las propiedades relevantes de ViewModel:

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

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

¿Alguna idea sobre cómo detener mis vistas anulando los valores en mi ViewModels? Estoy pensando que tal vez necesite establecer el DataContext de la Vista para que sea nulo antes de que se cierre, pero no estoy seguro de cómo hacerlo con la forma en que las cosas son actualmente vinculantes.

Respuesta

0

He encontrado una posible solución, pero realmente no me gusta.

Resulta que DataContext IS se ha establecido en nulo, pero eso no ayuda. Ocurre antes de que la propiedad se establezca en nulo. Lo que parece estar sucediendo es que los enlaces de datos no se eliminan antes de que UserControl/View se deshaga de sí mismo, por lo que el valor nulo se propaga cuando se elimina el control.

Por eso, cuando los cambios DataContext, si el nuevo contexto es nulo entonces elimino los enlaces correspondientes en el cuadro combinado, de la siguiente manera:

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

No soy un gran fan de este método, ya que significa Tengo que recordar hacerlo por cada control de datos que uso. Si hubiera una manera en la que podría tener cada UserControl simplemente eliminar sus enlaces automáticamente cuando cierren eso estaría bien, pero no puedo pensar en ninguna forma de hacerlo.

Otra opción podría ser simplemente reestructurar mi aplicación, para que las Vistas no se destruyan hasta que ViewModels lo haga, esto evitaría el problema por completo.

+0

He encontrado este mismo problema. Establecer el DataContext de niños visuales para null lo resolvió parcialmente. Ocultar la vista en lugar de destruirla no importaba. Todavía estoy buscando una solución completa. – HappyNomad

0

Cuando muestro un control de usuario diferente, me quedo con el modelo de vista de el control previo activo, pero los Vistas son descartados automáticamente por WPF.

El problema que estoy teniendo es que cuando la vista se apaga, cualquier bidireccional enlaces con las propiedades de los modelo de vista se ponen inmediatamente a nulo, así que cuando puedo mostrar la ViewModel de nuevo todos los valores solo se establecen en para anular en la interfaz de usuario.

No soy experto ni en WPF ni en MVVM, pero algo de esto no suena bien. Tengo problemas para creer que la eliminación de WPF de la vista está causando su problema. Por lo menos, en mi experiencia limitada, nunca me pasó algo así. Sospecho que el culpable es código en el modelo de vista o el código que cambia el modelo de vista que se usa para el contexto de datos.

0

Después de intentar detener la configuración nula por varios medios, me rendí y en su lugar lo conseguí trabajando de la siguiente manera. Hice el ViewModel de solo lectura antes de cerrar su vista. Lo logro en mi clase ViewModelBase, donde agregué una propiedad booleana IsReadOnly. Luego, en ViewModelBase.SetProperty() (ver a continuación), ignoro cualquier cambio de propiedad cuando IsReadOnly es verdadero.

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

Parece que funciona así, aunque todavía me gustaría saber una solución mejor.

0

Tuve el mismo problema. Lo que funcionó para mí fue eliminar UpdateSourceTrigger = PropertyChanged desde SelectedValueBindings. UpdateSourceTriggers PropertyChanged parecen disparar sobre una propiedad compartida de cierre de puntos de vista cuando se utiliza ese patrón:

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

Container Vista:

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

de contenedores modelo de vista:

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

Ajuste el UpdateSourceTrigger explícita a LostFocus

Si la vista está cerrando y establece sus datos a cero, no tiene ningún efecto sobre sus datos en el modelo de vista.

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