简体   繁体   中英

Create a binding from a viewmodel collection class to a property in the datacontext

I have a viewmodel with multiple counters which are used in serval methods. In the view model there is also a collection of class MenuItem which holds information the create the dynamic menuitems in the ribbon. On some of those menuitems i want to display the counter through a badge.

But to do this i need the bind the badge to the counter property. In my menuitem class i have the path for the binding, but how can i thell my menuitem template to bind to the path it has in it's own binding.

Examples are simplified

public class ViewmodelSample
{
    public int counter1 { get; set; }

    public ICollection<MenuItem> MenuItems { get; set; } = new ObservableCollection<MenuItem>();

    public void Sample()
    {
        MenuItems.Add(new MenuItem()
        {
            Name = "Test button",
            CounterPath = "counter1"
        });
    }


    public class MenuItem
    {
        public string Name { get; set; }
        public string CounterPath { get; set; }
    }
}
<ItemsControl ItemsSource="{Binding Path=MenuItems}" >
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Name}"/>
                <TextBlock Text="{Binding Path={Binding CounterPAth}}"/>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

You cannot bind the Path property in the Binding markup extension, it is not a DependencyProperty .

You could identify the target counter with eg a Counter property of type int . Apply a Style to your TextBlock with triggers that provide the bindings to the corresponding counter properties on the ViewmodelSample . You need a RelativeSource binding as the counters are in the parent DataContext .

<Style x:Name="CounterTextBlockStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
   <Style.Triggers>
      <DataTrigger Binding="{Binding Counter}" Value="1">
         <Setter Property="Text" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.counter1}"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding Counter}" Value="2">
         <Setter Property="Text" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.counter2}"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding Counter}" Value="3">
         <Setter Property="Text" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.counter3}"/>
      </DataTrigger>
   </Style.Triggers>
</Style>

For now i have created a helper class. Maybe is not perfect but this works for me (for now).

Helper class

    public class DynamicBindingHelper
{
    public string PropertyNaam { get; set; }



    public static string GetPath(DependencyObject obj)
    {
        return (string)obj.GetValue(PathProperty);
    }

    public static void SetPath(DependencyObject obj, string value)
    {
        obj.SetValue(PathProperty, value);
    }

    public static readonly DependencyProperty PathProperty =
        DependencyProperty.RegisterAttached("Path", typeof(string), typeof(DynamicBindingHelper), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnPathChanged)));


    private static void OnPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue == null || (e.NewValue is string val && val.IsNullOrWhiteSpace()))
            return;

        if (d == null)
            return;

        var binding = new Binding($"DataContext.{e.NewValue}")
        {
            RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(UserControl), 1),
            Mode=BindingMode.OneWay
        };

        switch (d)
        {
            case TextBlock _:
                BindingOperations.ClearBinding(d, TextBlock.TextProperty);
                BindingOperations.SetBinding(d, TextBlock.TextProperty, binding);
                break;
            case Badge _:
                BindingOperations.ClearBinding(d, Badge.ContentProperty);
                BindingOperations.SetBinding(d, Badge.ContentProperty, binding);
                break;
        }
    }
}

WPF usage

<dx:Badge helper:DynamicBindingHelper.Path="{Binding TellerNaam}" Padding="2,2,2,3" FontSize="10" Margin="-3,5,3,-5" />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM