控件可见性与ViewModel属性的绑定不起作用

本文关键字:绑定 不起作用 属性 ViewModel 可见性 控件 | 更新日期: 2023-09-27 18:02:49

我正在将一个相当大的WPF应用程序从三层转换为MVVM,并在学习MVVM的过程中。到目前为止,我还没有深入研究绑定(等等)的太多细节,所以请耐心等待。

我试图将多个控件的System.Windows.Visibility绑定到ViewModel的公共属性("State")。当父TabItem加载时,读取State属性并按需要进行处理。但是,当对该属性进行后续更改时,它们似乎会被忽略。我检查了绑定,调试了转换器,等等,这些都快把我逼疯了。

ViewModel:

public class MarketingListViewModel: IDisposable, INotifyPropertyChanged
{
    private UiState state;
    public event PropertyChangedEventHandler PropertyChanged;
    public UiState State 
    {
        get { return state; }
        set
        {
            if (state != value)
            {
                state = value;
                NotifyPropertyChanged("State");
            }
        }
    }
    public MarketingListViewModel() 
    {
        State = UiState.View;
    }
    private void NotifyPropertyChanged(String info) 
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

视图:

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
         mc:Ignorable="d" x:Class="WpfCrm.tabListManager" xmlns:DPH="clr-namespace:DPH" >
<UserControl.Resources>
    <DPH:MarketingListViewModel x:Key="listVM" />
    <!-- Note that the above line is giving me an "Object reference not set to an instance of an object" error -->
</UserControl.Resources>
<Grid x:Name="gridMain" DataContext="{StaticResource listVM}" >
    <Border Grid.Row="0" Grid.Column="1" Margin="10"
            Style="{StaticResource WidgetStyle}" >
        <Grid x:Name="gridListManagement" >
            <Label x:Name="labelManageLists" Content="Manage Lists" MouseDown="labelManageLists_MouseDown"
                Style="{StaticResource WidgetTitleStyle}"
                Grid.Row="0" Grid.Column="0" />
            <StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal"  HorizontalAlignment="Right" >
                <Label x:Name="llNewList" Content="new" MouseDown="llNewList_MouseDown"
                    Style="{StaticResource LinkLabelStyle}"
                    HorizontalAlignment="Right" />
                <Label x:Name="llCloseManageLists" Content="close" MouseDown="llCloseManageLists_MouseDown" 
                    Style="{StaticResource LinkLabelStyle}"
                    HorizontalAlignment="Right" />
            </StackPanel>
            <Label x:Name="labelListName" Content="Name" Grid.Row="1" Grid.Column="0" />
            <Grid Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" >
                <ComboBox x:Name="cbLists" SelectedIndex="-1" SelectionChanged="cbLists_SelectionChanged" IsReadOnly="True"
                          ItemsSource="{Binding Path=AllMarketingLists}" 
                          DisplayMemberPath="Name" 
                          SelectedValuePath="Id"
                          Visibility="{Binding Path=State, Converter={StaticResource ViewStateToVisibilityConverter} }"/>
                <TextBox x:Name="tbListName" 
                         Text="{Binding Path=OList.Name}"
                         Visibility="{Binding Path=State, Converter={StaticResource EditStateToVisibilityConverter} }"/>
            </Grid>
            <Label x:Name="labelListDescription" Content="Description" Grid.Row="2" Grid.Column="0" />
            <Grid Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" >
                <TextBlock x:Name="textblockListDescription" TextWrapping="Wrap" 
                           Text="{Binding Path=OList.Notes}"
                           Visibility="{Binding Path=State, Converter={StaticResource ViewStateToVisibilityConverter} }"
                           Grid.ColumnSpan="2" />
                <TextBox x:Name="tbListDescription" TextWrapping="Wrap"
                         Text="{Binding Path=OList.Notes}"
                         Visibility="{Binding Path=State, Converter={StaticResource EditStateToVisibilityConverter} }"
                         Grid.ColumnSpan="2" />
            </Grid>
            <StackPanel Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Right" Orientation="Horizontal" >
                <Button x:Name="buttonEditList" Content="Edit" Click="buttonEditList_Click"
                        Visibility="{Binding Path=State, Converter={StaticResource ViewStateToVisibilityConverter} }"
                        Width="60" Margin="3" />
                <Button x:Name="buttonSaveList" Content="Save" Click="buttonSaveList_Click"
                        Visibility="{Binding Path=State, Converter={StaticResource EditStateToVisibilityConverter} }"
                        Width="60" Margin="3" />
                <Button x:Name="buttonCancel" Content="Cancel" Click="buttonCancel_Click" 
                        Visibility="{Binding Path=State, Converter={StaticResource EditStateToVisibilityConverter} }"
                        Width="60" Margin="3" />
            </StackPanel>
        </Grid>
    </Border>
</Grid>

后面的代码有一些方法,比如:

    private void buttonEditList_Click(object sender, RoutedEventArgs e) 
    {
        listVM.State = UiState.Edit;
    }

有没有人知道为什么控件在状态改变后不更新它们的可见性?

谢谢,

衰变时

EDIT—Converters:

[ValueConversion(typeof(WpfCrm.UiState), typeof(System.Windows.Visibility))]
public class EditStateToVisibilityConverter: IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        UiState state = (UiState)value;
        if (state == UiState.View) return Visibility.Collapsed;
        else return Visibility.Visible;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}
[ValueConversion(typeof(WpfCrm.UiState), typeof(System.Windows.Visibility))]
public class ViewStateToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        UiState state = (UiState)value;
        if (state == UiState.View) return Visibility.Visible;
        else return Visibility.Collapsed;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

控件可见性与ViewModel属性的绑定不起作用

您似乎使用了视图模型的两个不同实例,其中一个在XAML中声明和使用

<UserControl.Resources>
    <DPH:MarketingListViewModel x:Key="listVM" />
</UserControl.Resources>
<Grid DataContext="{StaticResource listVM}" >

和一个在代码后面(来自注释):

listVM = new MarketingListViewModel(); 

你当然应该只用一个。因此,将声明后面的代码更改为

listVM = (MarketingListViewModel)Resources["listVM"]; 

好的,这是一个错误。将listVM声明为

var listVM = new MarketingListViewModel();

但这不是XAML中的listVM。在XAML中,您已经创建了MarketingListViewModel的另一个实例。因此,当您尝试更改在代码中声明的listVM时,什么也不会发生,因为该对象不是网格的DataContext。

在Click处理程序中,您必须编写以下内容:

private void buttonEditList_Click(object sender, RoutedEventArgs e) 
{
    var _listVM = (MarketingListViewModel)FindResource("listVM");
    _listVM.State = UiState.Edit;
}

或者用下面的代码替换你的listVM声明:

listVM = (MarketingListViewModel)FindResource("listVM");

那么你就不需要更改事件处理程序了。

希望有帮助。