none
How to bind a WPF UserControl's Visibility property RRS feed

  • Question

  • A WPF UserControl has been created and this View's DataContext is loaded with a ViewModel class. The object is to bind the Visibility property of the UserControl to a property in the ViewModel. The ViewModel class implements the INotifyChanged interface. The problem is, when the VM's property changes the UserControl doesn't ask for the property's value. However, if that same property is instead bound to a UI element with the UserControl then the property affects the visibility of that child UI element.

    The UserControl is very simple. The important part of the Visibility binding is this:

    <UserControl 
        <snipped for brevity>
        Visibility="{Binding VisibleState}"
        >
        <snipped for brevity>
    </UserControl>

    The code behind is where the ViewModel/DataContext is created and asserted to the View/UserControl. This is done this way because the application has to be small footprint so it simply is not feasible to bring in the full blown Prism/Unity/MVVM architecture but I still want to maintain separation of responsibilities as much as is practical.

    public AccountNoneView()
    {
        DataContext = new AccountNoneViewModel();
        InitializeComponent();
    }

    These WPF UserControls are hosted in a ViewBox which in turn is hosted in an ElementHost which in turn is a child of a WinForm window.

    Because I'm lazy and because I have a lot of these little UserControl/ViewModel things to do I created a BaseModel class that does the implementation of the INotifyPropertyChanged interface. It's the standard sort of thing.

    public class BaseModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void OnPropertyChanged( string name = "" )
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler( this, new PropertyChangedEventArgs( name ) );
            }
        }
    }

    The ViewModel looks like this (again, snipped for brevity).

    public class AccountNoneViewModel : BaseModel
    {
        public Visibility VisibleState
        {
            get { return _VisibleState; }
            set
            {
                if (_VisibleState != value)
                {
                    _VisibleState = value;
                    OnPropertyChanged( "VisibleState" );
                }
            }
        }
        private Visibility _VisibleState = Visibility.Hidden;
    
        public void SetVisibility( bool visible )
        {
            if (visible)
                VisibleState = System.Windows.Visibility.Visible;
            else
                VisibleState = System.Windows.Visibility.Collapsed;
        }

    The default value for the VisibilityState property is Hidden. This is on purpose. That way the very first value asserted to the property is guaranteed to issue a changed notification since only Visible and Collapsed values ever get set.

    If the UserControl's Visibility property is explicitly set in XAML to Collapsed then it is hidden and Visible it is shown so I know the UC is paying attention to the attribute values.

    When the control is first instantiated the UC calls into the property so I know that the DataContext is pointing at the VM class and that the Visible attribute is bound to the VisibleState property in the VM. I know this because if I set a break point at the getter it gets hit. However, when the property change notification goes out from the VM the UC never asks for it.

    So here's the sequence of events. The WinForm starts up. When it becomes visible it creates the ElementHost and adds it to the Form's Controls collection. The newly created ElementHost is populated with a UserControl that contains a number of smaller WPF UserControls, communications between which are established at run time. As the data values change the various child UserControls show and hide themselves as a result of the VisibleState properties in their responspective VMs. That's the idea, anyway. The reality is that data values change in the expected fashion with the exception of the Visibility attribute of the UserControls themselves. What am I doing wrong? Why doesn't the UserControl's Visibility attribute receive the change notification and respond to it?


    Richard Lewis Haggard


    Wednesday, May 30, 2018 2:29 AM

Answers

  • Thank you for your kind response. However, if you were to read the very first few lines of the original post you might notice that it says the problem is with the Visibility property of the UserControl and that there is no such problem with binding to elements within the UserControl. While I certainly appreciate the care and effort you expended on your response it does not address the problem. In any case I worked around the issue by going straight at the View object itself rather than the MVVM method of binding to a property. Yes, yes, I know it was poor technique but I'm on a schedule here and must keep my client happy. I'll figure out what is wrong with the code some other time.

    Short answer/hack: Create the ViewModel in the View's constructor. Pass a reference to the View in on the ViewModel's own constructor. Save the passed in View reference to a property within the ViewModel. When time comes to assert a Visibility use the View property to receive the Visibility state. Be sure to perform some sort of penance later in order to apologize to the coding gods for violating MVVM practices.


    Richard Lewis Haggard

    Tuesday, June 5, 2018 1:29 AM

All replies

  • Hi Richard,

    According to your description, you have some issue about change usercontrol's visibility using binding, if yes, you can take a look the following code, it uses converter.

    https://stackoverflow.com/questions/32081153/viewmodel-property-not-getting-updated-on-changing-textbox-text

    Best Regards,

    Cherry


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, May 31, 2018 9:00 AM
    Moderator
  • I'm not sure why it would appear that the supplied link was in any way related to the question. Should you have the time feel free to actually read the question and try again.

    Richard Lewis Haggard

    Thursday, May 31, 2018 11:57 PM
  • Hi Richard,

    You want to bind a WPF UserControl's Visibility property and have some issue, now I do one sample that want to bind sample control's visibility property, you can take a look:

    xaml:

     <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <TextBox
                x:Name="usercontrol1"
                Width="300"
                Height="50"
                Visibility="{Binding visiblestate}" />
            <Button
                x:Name="btn1"
                Grid.Row="1"
                Width="200"
                Height="30"
                Click="btn1_Click"
                Content="clickme" />
            <TextBox
                Grid.Row="2"
                Width="200"
                Height="30"
                Text="{Binding visiblestate}" />
        </Grid>

    .cs:

     public partial class Window6 : Window
        {      
            public string visiblestate
            {
                get { return (string)GetValue(visiblestateProperty); }
                set { SetValue(visiblestateProperty, value); }
            }
            // Using a DependencyProperty as the backing store for visiblestate.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty visiblestateProperty =
                DependencyProperty.Register("visiblestate", typeof(string), typeof(Window6), new PropertyMetadata("Hidden"));
            public Window6()
            {
                InitializeComponent();
                this.DataContext = this;
            }
            private void btn1_Click(object sender, RoutedEventArgs e)
            {
                visiblestate = "Visible";
            }
          
        }

    Best Regards,

    Cherry


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, June 1, 2018 9:05 AM
    Moderator
  • Thank you for your kind response. However, if you were to read the very first few lines of the original post you might notice that it says the problem is with the Visibility property of the UserControl and that there is no such problem with binding to elements within the UserControl. While I certainly appreciate the care and effort you expended on your response it does not address the problem. In any case I worked around the issue by going straight at the View object itself rather than the MVVM method of binding to a property. Yes, yes, I know it was poor technique but I'm on a schedule here and must keep my client happy. I'll figure out what is wrong with the code some other time.

    Short answer/hack: Create the ViewModel in the View's constructor. Pass a reference to the View in on the ViewModel's own constructor. Save the passed in View reference to a property within the ViewModel. When time comes to assert a Visibility use the View property to receive the Visibility state. Be sure to perform some sort of penance later in order to apologize to the coding gods for violating MVVM practices.


    Richard Lewis Haggard

    Tuesday, June 5, 2018 1:29 AM
  • I was able to get this to work.  Here are the differences but not sure which will make it work.

    • Set DataContext in the UserControl header, before Visibility
    • Did not set DataContext in code-behind
    • Used a VisibilityToBooleanConverter, set up in the App.xaml
    • ViewModel property is a bool instead of Visibility
    • The usercontrol is preloaded into a DockPanel but isn't shown because the ViewModel prop initializes to false.
    Hope it helps.
    Thursday, October 10, 2019 8:40 PM