tags:

views:

172

answers:

1

Hey there.

I'm having an issue when trying to do something which should be as easy as. I've attempted to use a trigger based on a dependency property or a data trigger - I can't get either to work.

XAML for the trigger is:

<Style x:Key="FileWatchButton" BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
    <Style.Triggers>
        <Trigger Property="Main:Main.XmlFilesAvailableForLoading" Value="True">
            <Setter Property="Background" Value="Red" />
        </Trigger>
    </Style.Triggers>
</Style>

And the associated code-behind is:

public static readonly DependencyProperty XmlFilesAvailableForLoadingProperty =
DependencyProperty.Register("XmlFilesAvailableForLoading", typeof(bool), typeof(Main));

public bool XmlFilesAvailableForLoading
{
    get
    {
        try
        {
            return (bool)this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.DataBind,
     (System.Windows.Threading.DispatcherOperationCallback)delegate { return GetValue(XmlFilesAvailableForLoadingProperty); },
     XmlFilesAvailableForLoadingProperty);
        }
        catch (Exception)
        {
            return (bool)XmlFilesAvailableForLoadingProperty.DefaultMetadata.DefaultValue;
        }
    }
    set
    {
        this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.DataBind,
    (System.Threading.SendOrPostCallback)delegate{ SetValue(XmlFilesAvailableForLoadingProperty, value); }, value);
    }
}

Basically the dp is being set correctly by the presenter (it's based on a FileSystemWatcher class looking for one or more files) but the trigger is not being fired. Is this a threading issue?

Thanks.

+1  A: 

It's not clear if the code is complete, but it looks like the Property path in your trigger may be wrong. Does the button being styled have a Main property? I am guessing not; it looks like you are trying to trigger on a property of a different element, called Main -- is that right?

In any case, the namespace prefix is not required. If the button has a property named Main, then you can address this directly; if it doesn't, then the prefix won't help you.

My guess is that you probably need a DataTrigger whose binding refers to the Main element:

<local:Main Name="MyMain" ... />  <!-- this has the XmlFilesAvailableForLoading property -->

<DataTrigger Binding="{Binding XmlFilesAvailableForLoading, ElementName=MyMain}"
             Value=True>
  <Setter Property="Background" Value="Red" />
</DataTrigger>

On an unrelated note, you should have any non-boilerplate implementation in your DP getter and setter. Remember that the binding and styling system will bypass the getter and setter and talk directly to the underlying storage. So I'd strongly advise changing these back to just plain GetValue and SetValue calls.

itowlson
If I have the standard getter/setter in my property then I get the exception:"The calling thread cannot access this object because a different thread owns it."I.e. I need to use a thread safe getter/setter.Main is the Window in which the Button that I'm attempting to style via a trigger is situated. I've tried the following DataTrigger, which appears (to me) to be syntactically correct but does not do anything:<DataTrigger Binding="{Binding XmlFilesAvailableForLoading}" Value="True" />P.s. the button doesn't have a Main property, I'm attempting to trigger on XmlFilesAvailable.
pFrenchie
Your DataTrigger is syntactically correct, but will look for a XmlFilesAvailableForLoading property in the local data context. Is that correct? From your code sample it looked like this property was on Main. If so, you can use ElementName as shown in my sample binding, or RelativeSource AncestorType, to get the DataTrigger binding to look for the property on the right place (the Main element).
itowlson
Just to reiterate, Main is the Window in which the controls are being hosted, and XmlFilesAvailableForLoading is a public property of the window - hence it should be of local data context. I've tried adding the ElementName as shown in your sample code but to no avail (i.e. it compiles and runs, but nothing happens).
pFrenchie
Don't forget that the DataContext is not (by default) the window; the DataContext is typically a model object. To access a property of the window class you will need to point the binding at the window instance. (Which suggests a better way out of your difficulty: move the FilesAvailable property off the window and onto a FileStatus data object, and set the DataContext to be that FileStatus object.) In the meantime, does the Output window show any binding errors for the ElementName or RelativeSource AncestorType bindings? That can be useful in diagnosing this kind of problem...
itowlson
That's fixed it - using your example DataTrigger I've added x:Name="MyMain" to the Window declaration and the trigger's firing as expected. Thanks!
pFrenchie