Delay expander binding before expanding

I have a window bound to a binding to a ViewModel. ViewModel has many properties, some of which are expensive and not always needed by the user.

I would like these properties to bind to controls contained in different non-extensible extenders. When the extender is being extended by the user, I would like the controls to bind and evaluate properties. If the user does not expand the expander, the properties should not be evaluated and no cost incurred.

I could do this using events from the expander, but I am trying to find a good MVVM way. Any suggestions?

+3


source to share


3 answers


Does something like this work?

<Expander Header="Diagnostics" IsExpanded="False">
    <Expander.Style>
        <Style TargetType="Expander">
            <Setter Property="DataContext" Value="{x:Null}" />
            <Style.Triggers>                                    
                <Trigger Property="IsExpanded" Value="True">
                    <Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Diagnostics}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Expander.Style>

    <TextBlock Text="{Binding TestValue}" />
</Expander>

      



If you want extended bindings to be the datacontext of the parent window, presumably this will work in a trigger:

<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext}" />

      

+1


source


@Alain's suggestion looks interesting.

Also, since your properties are expensive to evaluate, you should also look at asynchronous bindings and priority bindings.

Async example:

<TextBox Text={Binding Path=Description, IsAsync="True"}/>

      

Setting IsAsync = True will prevent the binding evaluation from blocking the UI thread.



You can also use priority binding, which is a form of multi-binding:

<Grid>
    <Grid.DataContext>
        <Samples:PriorityBindingViewModel/>
    </Grid.DataContext>
    <TextBox>
        <TextBox.Text>
            <PriorityBinding>
                <Binding Path="ExpensiveProperty" Mode="OneWay" IsAsync="True"/>
                <Binding Path="LessExpensiveProperty" Mode="OneWay" IsAsync="True"/>
                <Binding Path="LeastExpensiveProperty" Mode="OneWay" IsAsync="True"/>
            </PriorityBinding>
        </TextBox.Text>
    </TextBox>
</Grid>

      

Bindings are evaluated with priority from top to bottom. This allows the "loading ..." message to be specified in the lowest priority binding (LeastExpensiveProperty) while high priority but slow bindings are evaluated.

public class PriorityBindingViewModel
{
    private string _expensiveProperty;

    private string _lessExpensiveProperty;

    public PriorityBindingViewModel()
    {
        ExpensiveProperty = "Loaded";
        LessExpensiveProperty = "Still loading";
        LeastExpensiveProperty = "Loading";
    }

    public string ExpensiveProperty
    {
        get
        {
            // Thread.Sleep is in place of an 'expensive' operation.
            // Just in case anyone doesn't realize.
            Thread.Sleep(8000);  
            return _expensiveProperty;
        }
        private set { _expensiveProperty = value; }
    }

    public string LessExpensiveProperty
    {
        get
        {
            // Same here.
            Thread.Sleep(3000);
            return _lessExpensiveProperty;
        }
        private set { _lessExpensiveProperty = value; }
    }

    public string LeastExpensiveProperty { get; private set; }
}

      

+2


source


<Expander Expanded="Expander_Expanded">
   <....
</Expander>

in .cs:

private void Expander_Expanded(object sender, RoutedEventArgs e)
{
    var expander = (Expander)sender;

    if(expander.DataContext == null)
    expander.DataContext = ViewModel;
}

      

+1


source







All Articles