0
votes

I'm not very familiar with WPF complex binding and since yesterday I cant figure out how to bind a value from my code behind with "DataContext" inside a DataTrigger of a button inside a ListView.

I try to do that because I want to hide my actions buttons (edit/delete) of each row of my listview items. So I have to check from a "User" class, the role attribute of it and toggle the visibility of the buttons.

I red lots of topics on "wpf binding datacontext", and tried also tons of potential solution but nothing succeed.

Here is the solution that i kept because when I put a breakpoint on my DataContext I can see all the values inside.

Code behind .cs :

        public User _user { get; set; }
        public ObservableCollection<Item> MyItems { get; set; }
        public MyWindow(User user)
        {
            InitializeComponent();
            _user = user;
            DataContext = _user; 
            listItems.ItemsSource = GetItemsList();         
        }

MyWindow.xaml :

 <ListView x:FieldModifier="public" ItemsSource="{Binding MyItems}" Margin="10" Name="listItems" BorderThickness="2" BorderBrush="Black" Style="{StaticResource MaterialDesignListView}">
                        <ListView.View>
                            <GridView x:Name="gridViewItem">
                                <GridViewColumn Width="100" DisplayMemberBinding="{Binding Name}">
                                    <GridViewColumn.Header>
                                        <GridViewColumnHeader Cursor="Hand" Tag="Name">Name</GridViewColumnHeader>
                                    </GridViewColumn.Header>
                                </GridViewColumn>
                                <GridViewColumn Width="180" DisplayMemberBinding="{Binding StartHour}">
                                    <GridViewColumn.Header>
                                        <GridViewColumnHeader Cursor="Hand" Tag="StartHour">StartHour</GridViewColumnHeader>
                                    </GridViewColumn.Header>
                                </GridViewColumn>
                                <GridViewColumn Width="180" DisplayMemberBinding="{Binding EndHour}">
                                    <GridViewColumn.Header>
                                        <GridViewColumnHeader Cursor="Hand" Tag="EndHour">EndHour</GridViewColumnHeader>
                                    </GridViewColumn.Header>
                                </GridViewColumn>                                
                                <GridViewColumn Width="180">
                                    <GridViewColumn.Header>
                                        <GridViewColumnHeader Cursor="Hand" Tag="Actions">Actions</GridViewColumnHeader>
                                    </GridViewColumn.Header>
                                    <GridViewColumn.CellTemplate>
                                        <DataTemplate>
                                            <StackPanel Orientation="Horizontal" Name="ActionButtonList">                                   
                                                <Button Click="Delete_Click" x:Name="delete_button" BorderBrush="{x:Null}" Tag="{Binding Id}" Background="White" Margin="0" HorizontalAlignment="Left">
                                                    <materialDesign:PackIcon Kind="Trash" Foreground="Black" Width="15" Height="15" />
                                                    <Button.Style>
                                                        <Style TargetType="{x:Type Button}">
                                                            <Setter Property="Visibility" Value="Visible"/>
                                                            <Style.Triggers>
                                                                <DataTrigger Binding="{Binding DataContext.Role}" Value="User">
                                                                    <Setter Property="Visibility" Value="Collapsed"/>
                                                                </DataTrigger>
                                                            </Style.Triggers>
                                                        </Style>
                                                    </Button.Style>
                                                </Button>
                                            </StackPanel>
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                            </GridView>
                        </ListView.View>
                    </ListView>

User.cs :

public partial class User
    {       
        public User()
        {}

        public int Id { get; set; }


        public string Role { get; set; }

    }

The debug error : System.Windows.Data Error: 40 : BindingExpression path error: 'DataContext' property not found on 'object' ''MyItem_90931F6CDC904A032778416D7F3A5FB410232AF48F7B7245FOE8D783347E9ED5' (HashCode=34262603)'. BindingExpression:Path=DataContext.Role; DataItem='MyItem_90931F6CDC904A032778416D7F3A5FB410232AF48F7B7245FOE8D783347E9ED5' (HashCode=34262603); target element is 'Button' (Name=''); target property is 'NoTarget' (type 'Object')

I think the problem is caused by the ambiguous use of DataContext, it recognize the internal DataContext of the ListView and not the external one, from the code behind.

Thanks for your attention and your help

2

2 Answers

0
votes

It's not ambiguous, DataContext deos'nt exist at the level you resquesting it. What you should do.

  • Create a class called MyWindowViewModel
  • This class should have both User and MyItems as Properties
  • Set MyWindow.DataContext to MyWindowViewModel (do not set ItemsSource in the code behind)
  • In the xaml code keep ItemsSource biding to MyItems
  • Replace [Binding="{Binding DataContext.Role}"] by something like ["{Binding Path=DataContext.User.Role, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"]

You should also think about using Icommand instead of click event and code behind, and if just desabling the button instead of hidding it is fine, you an use ICommand for that (see https://www.c-sharpcorner.com/UploadFile/20c06b/icommand-and-relaycommand-in-wpf/)

0
votes

Finally I made it, the solution was pretty simple : My code behind is good but the problem was my Window XAML, I had to set a Name to my Window then in my DataTrigger it looks like this :

<Button Click="Delete_Click" x:Name="delete_button" BorderBrush="{x:Null}" Tag="{Binding Id}" Background="White" Margin="0" HorizontalAlignment="Left">
                                                    <materialDesign:PackIcon Kind="Trash" Foreground="Black" Width="15" Height="15" />
                                                    <Button.Style>
                                                        <Style TargetType="{x:Type Button}">
                                                            <Setter Property="Visibility" Value="Visible"/>
                                                            <Style.Triggers>
                                                                <DataTrigger Binding="{Binding ElementName=CertifWindow,Path=DataContext.Role}" Value="User">
                                                                    <Setter Property="Visibility" Value="Collapsed"/>
                                                                </DataTrigger>
                                                            </Style.Triggers>
                                                        </Style>
                                                    </Button.Style>
                                                </Button>

Hope it will help someone