【问题标题】:StackPanel visibility not updated when dependency property changed依赖属性更改时 StackPanel 可见性未更新
【发布时间】:2014-10-28 19:48:54
【问题描述】:

我目前正在使用 MVVM(不是 MVVM Light)在 C#/XAML 中开发一个通用应用程序,但我在 XAML 部分遇到了麻烦。

当我的 ViewModel 中的依赖项属性发生更改时,我想显示一个或另一个 StackPanel。我认为代码不言自明。

<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource BooleanToVisibilityConverter}}">
    <!-- Some content -->
</StackPanel>

<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource InvertBooleanToVisibilityConverter}}">
    <!-- Some another content -->
</StackPanel>

这里是依赖属性的定义。

public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
    "MyProperty",
    typeof (bool),
    typeof (MyViewModel),
    new PropertyMetadata(true));

public bool MyProperty
{
    get { return (bool) GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); OnPropertyChanged(); // Implemented by ReSharper }
}

我猜你知道MyProperty 是一个布尔值,我通过转换器将它转换为Visibility。因此,当 ViewModel 中的 MyProperty 发生更改时,视图不会更新。

我已经尝试使用 UpdateSourceTrigger 属性,但它不起作用。另外,我没有绑定错误,转换器工作正常(我在应用启动时只看到一个StackPanel)。

请记住,除非没有其他解决方案,否则我不想使用后面的代码。

感谢您的帮助。

【问题讨论】:

  • 你可以为你的依赖属性添加定义吗?
  • 你通知属性改变了吗?
  • @AaronHawkins:当然。我刚刚编辑了初始帖子。
  • @ChrisW。 : 你能说得更具体点吗?

标签: c# xaml mvvm win-universal-app


【解决方案1】:

我最终放弃并使用了部分代码,现在它工作正常。

【讨论】:

    【解决方案2】:

    您的&lt;StackPanel&gt;s 是某个 UserControl 的一部分吗?如果不是,你为什么使用DependencyProperty

    你的实现也很差。

    让我们假设这不是自定义控件的一部分(纠正我——如果我错了,我会重写解决方案)

    所以你有一个 ViewModel 并且你想将一些属性连接到它。你真的不需要实现 DependencyProperty 来做你想做的事,但我会以你的方式实现它来娱乐你。

    这是一个具有 1(一个)属性的示例 ViewModel

    using Windows.UI.Xaml;
    using System.ComponentModel;
    
    // very simple view model
    class MyViewModel : DependencyObject, INotifyPropertyChanged
    {
    
        // implement INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        } 
    
        // register
        public static DependencyProperty FooterTitleProperty = DependencyProperty.Register("FooterTitle", typeof(string), typeof(MyViewModel),
             new PropertyMetadata(string.Empty, OnFooterTitlePropertyChanged));
    
        // the actual property
        public string FooterTitle
        {
            get { return (string) GetValue(FooterTitleProperty); }
            set
            {
                SetValue(FooterTitleProperty, value);
            }
        }
    
        // this will fire when the property gets change
        // it will call the OnPropertyChanged to notify the UI element to update its layout
        private static void OnFooterTitlePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            MyViewModel mvm = dependencyObject as MyViewModel;
            mvm.OnPropertyChanged("FooterTitle");
        }
    }
    

    为了测试代码,我们将制作一个非常简单的 XAML 表单

    <Grid x:Name="ContentPanel">
        <StackPanel>
            <TextBlock x:Name="tb" Text="{Binding FooterTitle}" FontSize="48"></TextBlock>
            <Button Content="Test Property" Click="Button_Click_1"></Button>
        </StackPanel>
    </Grid>
    

    当您单击按钮时,我们将更改文本框的文本

    public sealed partial class MainPage : Page
    {
        // create the view model
        MyViewModel vm = new MyViewModel();
    
        public MainPage()
        {
            this.InitializeComponent();
    
            this.NavigationCacheMode = NavigationCacheMode.Required;
    
    
            // set the text we initial want to display
            vm.FooterTitle = "default text";
    
            // set the DataContext of the textbox to the ViewModel
            tb.DataContext = vm;
        }
    
    
        // after the button is click we change the TextBox's Text
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            // change the text
            vm.FooterTitle = "Test Property Has Changed.";
    
            // what happens is the Setter of the Property is called first
            // after that happens it launches the `OnFooterTitlePropertyChanged` event
            // that we hook up with the Register function.
    
    
            // `OnFooterTitlePropertyChanged` launches the INotifyPropertyChanged event
            // then finally the TextBox will updates it's layout
        }
    }
    

    此时你可以猜到你真的不需要 DependencyProperty 并说为什么我不能直接在 Setter 中启动 INotifyPropertyChanged 呢?好吧,你可以,这可能是首选方法。

    如果所有这些都是 UserControl 的一部分,那么我可以看到使用 DependencyProperty 然后在 OnFooterTitlePropertyChanged 事件中您可以设置

    name_of_textbox.Text = FooterTitle;
    

    【讨论】:

    • 感谢您的回答,但这不是我想要的。老实说,目前我的StackPanels 不是自定义UserControl 的一部分,但它们会在以后(我处于开发的早期阶段)。另外,我想避免使用部分代码,因为它是一个通用应用程序,我不想复制/粘贴代码。当然,如果有其他解决办法,我就照你说的做。
    • @Atlasmaybe 后面没有代码(那只是为了测试虚拟机)。该 VM 可以是共享项目的一部分。用您的 StackPanel 替换 TextBox,您将看到一旦您的属性更改,它将启动您的转换器。我编写的示例是为了让您进入正确的方向,因为您的问题和示例代码没有什么意义。
    • 我分析了你的代码,它和我的一样:你有一个带有 DP 的简单 VM,一个简单的视图。区别在于OnPropertyChanged() 事件启动和ButtonClickEvent(这是您创建的页面背后的代码的一部分),您用于更改属性,而我在VM 中更改。感谢您为将我引向“正确方向”所做的努力,但我认为如果我们拥有相同的代码,这将无济于事。
    【解决方案3】:

    我认为属性名称应该用 OnPropertyChanged 方法给出,像这样;

    public bool MyProperty
    {
        get { return (bool) GetValue(MyPropertyProperty); }
        set { 
                  SetValue(MyPropertyProperty, value); 
                  OnPropertyChanged("MyProperty"); 
            }
    }
    

    https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.inotifypropertychanged.propertychanged

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-23
      • 2011-07-10
      • 2011-12-18
      • 2017-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多