WPF View устанавливает нулевые свойства ViewModel при закрытии - PullRequest
7 голосов
/ 16 июня 2009

У меня есть приложение, в котором я отображаю пользовательские элементы управления в GroupBox. Чтобы отобразить элементы управления, я связываюсь со свойством в ViewModel главной формы, которое возвращает ViewModel для отображения. Я настроил DataTemplates так, чтобы форма автоматически знала, какой UserControl / View использовать для отображения каждой ViewModel.

Когда я отображаю другой UserControl, я сохраняю активным ViewModel предыдущего элемента управления, но WPF автоматически отбрасывает представления.

Проблема, с которой я сталкиваюсь, заключается в том, что при закрытии представления любые двусторонние привязки к свойствам в ViewModel немедленно устанавливаются в null, и поэтому, когда я снова отображаю ViewModel, все значения просто устанавливаются в нуль в пользовательском интерфейсе.

Я предполагаю, что это потому, что как часть представления закрывается, он удаляет и очищает любые значения в элементах управления, которые он содержит, и поскольку привязки на месте, они распространяются и на ViewModel.

Шаблоны данных в моих ресурсах

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

Код, используемый для отображения пользовательских элементов управления

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

Пример элемента управления, который я связываю в одном из представлений:

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

и соответствующие свойства ViewModel:

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

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

Есть идеи, как остановить мои представления, обнуляющие значения в моих моделях представления? Я думаю, что, возможно, мне нужно установить для DataContext представления значение null, прежде чем оно закроется, но я не уверен, как это сделать с тем, как все в настоящее время является обязательным.

Ответы [ 5 ]

0 голосов
/ 16 июля 2015

Установите UpdateSourceTrigger в явном виде на LostFocus

Если представление закрывается и устанавливает его данные в null, это не влияет на ваши данные в модели представления.

<ComboBox Name="SupervisorDropDown" ItemsSource="{Binding Path=Supervisors}" DisplayMemberPath="sgSupervisor" 
SelectedValuePath="idSupervisor" 
SelectedValue="{Binding Path=SelectedSupervisorID, UpdateSourceTrigger=LostFocus}" />
0 голосов
/ 27 октября 2013

У меня была такая же проблема. Для меня сработало удаление UpdateSourceTrigger = PropertyChanged из моих SelectedValueBindings. PropertyChanged UpdateSourceTriggers, похоже, срабатывает при привязке свойств закрывающих представлений при использовании этого шаблона:

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

Вид контейнера:

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

Контейнер ViewModel:

TabCollection.Add(new WelcomeTabViewModel());
TabCollection.Add(new UserSecurityViewModel(_userService, _bpcsUsersLookup));
TabCollection.Add(new PackItemRegisterViewModel(_packItemService, _itemClassLookup));
SelectedTabIndex = 0;
0 голосов
/ 16 июня 2009

Когда я отображаю другой UserControl, я держу ViewModel из предыдущий элемент управления активен, но Просмотры отбрасываются автоматически WPF.

Проблема в том, что у меня когда вид закрывается, любые два пути привязки к свойствам в ViewModel немедленно устанавливается в ноль, и так, когда я отображаю ViewModel опять все значения просто установлены обнулить в пользовательском интерфейсе.

Я не эксперт ни по WPF, ни по MVVM, но кое-что по этому поводу звучит неправильно. Мне трудно поверить, что избавление от WPF вызывает вашу проблему. По крайней мере, в моем ограниченном опыте у меня никогда не было ничего подобного. Я подозреваю, что виновником является либо код в модели представления, либо код, изменяющий, какая модель представления используется для текста данных.

0 голосов
/ 23 ноября 2010

Попытавшись остановить настройку нуля различными способами, я сдался и вместо этого заставил его работать следующим образом. Я сделал ViewModel только для чтения, прежде чем закрыть его представление. Я выполняю это в своем классе ViewModelBase, где я добавил логическое свойство IsReadOnly. Затем в ViewModelBase.SetProperty () (см. Ниже) я игнорирую любые изменения свойств, когда IsReadOnly имеет значение 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;
    }

Кажется, это работает так, хотя я все еще хотел бы узнать лучшее решение.

0 голосов
/ 16 июня 2009

Я нашел одно возможное решение, но оно мне действительно не нравится.

Оказывается, DataContext IS уже имеет значение null, но это не помогает. Это происходит до того, как для свойства установлено значение null. Похоже, происходит то, что привязки данных не удаляются до того, как UserControl / View избавится от себя, и поэтому нулевое значение распространяется вниз при удалении элемента управления.

Поэтому, когда DataContext изменяется, если новый контекст имеет значение null, я удаляю соответствующие привязки в ComboBox следующим образом:

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

Я не большой поклонник этого метода, потому что это означает, что я должен помнить, чтобы делать это для каждого элемента управления с привязкой к данным, который я использую. Если бы был способ, я мог бы просто заставить каждый UserControl просто удалять их привязки автоматически, когда они закрываются, это было бы нормально, но я не могу придумать, как это сделать.

Другой вариант может состоять в том, чтобы просто реструктурировать мое приложение, чтобы представления не разрушались до тех пор, пока не будут выполнены ViewModels - это полностью обошло бы проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...