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

时间:2015-07-15 20:35:37

标签: c# wpf xaml mvvm

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

我正在尝试将多个控件的System.Windows.Visibility绑定到ViewModel的公共属性(“State”)。加载父TabItem时,将根据需要读取和处理State属性。但是,当对属性进行后续更改时,它们似乎会被忽略。我(重新重新)检查了Bindings,调试了转换器等,这让我发疯了。

视图模型:

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

在状态发生变化后,有没有人知道为什么控件没有更新其可见性?

谢天谢地,

DPH

编辑 - 转换器:

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

2 个答案:

答案 0 :(得分:2)

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

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

和一个代码隐藏(来自评论):

listVM = new MarketingListViewModel(); 

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

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

答案 1 :(得分:1)

好的,有一个错误。您将listVM声明为

var listVM = new MarketingListViewModel();

但这不是XAML中的listVM。在XAML中,您创建了另一个MarketingListViewModel实例。因此,当您尝试更改在代码中声明的listVM时,没有任何反应,因为此对象不是Grid的DataContext。

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

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

或者使用以下代码覆盖代码隐藏中的listVM声明:

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

然后您将不需要更改事件处理程序。

希望,这有帮助。