1

I have a strange problem with my WPF control. Basically I have a control which presents modal content in front of my primary content. My control is a modification of my answer shown here.

I have a Bool dependency property called IsShown which is used to hide / show the modal content. The property is defined like this:

public static readonly DependencyProperty IsShownProperty =
    = DependencyProperty.Register("IsShown",
    typeof(bool), 
    typeof(ModalContentControl), 
    new UIPropertyMetadata(false, IsShownChangedCallback));

It has a standard .Net property wrapper like so:

public bool IsShown
{
    get { return (bool)GetValue(IsShownProperty); }
    set { SetValue(IsShownProperty, value); }
}

The property changed call back looks like this:

private static void IsShownChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ModalContentControl control = (ModalContentControl)d;

    if ((bool)e.NewValue == true)
    {
        control.ShowModalContent();
    }
    else
    {
        control.HideModalContent();
    }
}

The ShowModalContent and HideModalContent methods contain the logic to show and hide the modal content respectively.

I have this control in my window and the IsShown property is bound to my viewModel. I can press a button which invokes a command that changes a boolean property that is bound to the IsShown property of my control. The modal content is shown and hidden as expected.

My problem comes from the fact that the ShowModalContent and HideModalContent methods are public so that they can be called from code. What I want to be able to do is call ShowModalContent, for example, and when I query the IsShown property it should report the correct value (in this case true) E.G. If I was using a regular property I would have a backing field of type Bool which would be updated in the method. This field would then be returned by the property get statement.

But because my property is a dependency property I don't have access to a backing field. The only way I can update the value from the method is to set the dependency property through the .net wrapper but this stops my control from working (once the content is shown I cannot get rid of it again).

I thought this behaviour might be caused by setting the IsShown property in the method, which calls the property changed callback, which sets the IsShown property which calls.. but I remember reading somewhere that WPF protects against this scenario.

So how do I fix this problem so that my boolean dependency property reports the correct value?

Community
  • 1
  • 1
Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100

1 Answers1

0

With a little bit of thought (and some luck) I managed to figure out what the problem was. There were 2 things I needed to change in order to get this to work.

The first problem was that the ShowModalContent and HideModalContent methods were doing two things.

  1. Setting the IsShown property.
  2. Updating the state of the control.

This was a problem because changing the IsShown property calls the IsChangedCallback which calls either the ShowModalContent and HideModalContent methods which then change the IsShown property. I think WPF stops an endless loop occurring but even trying to manage this in code by caching the values was a major headache.

My moment of inspiration came when I realised that the only thing the public method should be doing is setting the property. My public methods now look like this:

public void ShowModalContent()
{
    IsShown = true;
}

public void HideModalContent()
{
    IsShown = false;
}

This then triggers the property changed callback which updates the state of the control.

My second moment of inspiration was when I realised my ViewModel wasn't accurately reflecting the state of the IsShown property and that this was being caused by the fact that apparently the default dependency property binding mode is OneWay. I simply updated my DependencyProperty definitions binding mode to BindsTwoWayByDefault and everything now works fine.

IsShownProperty = DependencyProperty.Register("IsShown",
    typeof(bool), typeof(ModalContentControl), 
    new FrameworkPropertyMetadata(false, 
        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
        IsShownChangedCallback));
Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100