Hide button inside the ListView - c#

I have got a ListView and each row has a Button. I want to hide Buttons until the user selects any row.
I used in my ViewModel :
private Visibility _deleteButtonVisibility;
public Visibility DeleteButtonVisibility { get { return _deleteButtonVisibility; } set { _deleteButtonVisibility = value; OnPropertyChanged(nameof(DeleteButtonVisibility)); } }
In my Constructor
DeleteBtnVisibility = Visibility.Hidden;
XAML code i used this
<GridViewColumn Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate >
<StackPanel HorizontalAlignment="Center">
<Button Click="DeleteBand_Click" Visibility="{Binding DeleteBtnVisibility }" Content="Delete" Width="88"></Button>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.Header>
<GridViewColumnHeader Tag="Delete" >Delete</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
ViewModel event listener:
private void WarningModel_OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == nameof(SelectedWarning))
{
if(SelectedWarning != null)
{
DeleteBtnVisibility = Visibility.Visible;
}
}
}
binding is not working inside the ListView.
If i use the same binding any other objects outside of the ListView, it hides the object.
I tried to hide <StackPanel> which contains the Button but still no success.
I am not sure why Binding doesn't work

If the DeleteBtnVisibility property is defined in the view model of the ListView (or its parent view), you could bind to it using a RelativeSource:
<Button Click="DeleteBand_Click"
Visibility="{Binding DataContext.DeleteBtnVisibility, RelativeSource={RelativeSource AncestorType=ListView}}"
Content="Delete" Width="88" />

Related

Bind MenuItem.IsEnabled to property of a view model

I have an MVVM WPF project with the following code:
MultiplexerVM.cs
public class MultiplexerVM : BaseViewModel
{
public ObservableCollection<MultiplexVM> Multiplexes { get; set; } = new();
public MultiplexVM SelectedMultiplex { get; set; }
public ICommand CheckAll => new CheckBoxCommand(Multiplexes);
}
MultiplexVM.cs
public class MultiplexVM : BaseViewModel
{
public bool IsChecked { get; set; }
}
MultiplexerV.xaml
<UserControl x:Class="MKVStudio.Views.MultiplexerV"
xmlns:vm="clr-namespace:MKVStudio.ViewModels"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<UserControl.Resources>
<s:Boolean x:Key="True">True</s:Boolean>
<s:Boolean x:Key="False">False</s:Boolean>
</UserControl.Resources>
<Grid>
<ListView ItemsSource="{Binding Multiplexes}"
SelectedItem="{Binding SelectedMultiplex}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"Margin="3"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
</GridView>
</ListView.View>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding CheckAll}"
CommandParameter="{StaticResource True}">
<MenuItem.Header>
<TextBlock Text="Check all"/>
</MenuItem.Header>
</MenuItem>
<MenuItem Command="{Binding CheckAll}"
CommandParameter="{StaticResource False}">
<MenuItem.Header>
<TextBlock Text="Uncheck all"/>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
</Grid>
</UserControl>
My goal is to bind IsEnabled of the context menu items to the property IsChecked of MultiplexVM.cs. The idea was to implement an IValueConverter (passing Multiplexes as value and bool as parameter). The converter returns value.Where(m => m.IsChecked == parameter).Count > 0. Essentially, when all Multiplexes are unchecked the menu item Check all is enabled and the menu item Uncheck all is disabled. The reverse thing is happening when all Multiplexes are checked. The problem here is that the converter is invoked only once when it is declared basically, and checking and unchecking the items does not trigger the converter to see what is happening.
I have tried to implement an IMultiValueConverter (but failing to use it correctly) and pass three values like this:
<MenuItem.IsEnabled>
<MultiBinding>
<Binding Source="{Binding Multiplexes.Count}" />
<Binding Source="{Binding Multiplexes}" />
<Binding Source="{StaticResource True}" /> <!--respectivly False to the other menu item-->
</MultiBinding>
</MenuItem.IsEnabled>
This doesn't work. I've tried <Binding Path="Multiplexes.Count" /> and <Binding Path="Multiplexes" />, but also doesn't work (the values passed to the converter are Unset).
Is my idea for using MultiBinding even feasible and what am I doing wrong when using it?
Why do you need to bind IsChecked to IsChecked and IsEnabled at once? This is very strange if you look at it from the Single Responsibility Principle. If you are sure that you are doing it right, you can do it like this:
<CheckBox IsChecked="{Binding IsChecked}"
IsEnabled="{Binding IsEnabled}" />
And make your class look like something like this:
public class MultiplexVM : BaseViewModel
{
public bool IsChecked
{
get => isChecked;
set
{
isChecked = value;
isEnabled = value;
RaisePropertyChanged(nameof(IsChecked));
RaisePropertyChanged(nameof(IsEnabled));
};
}
private bool isChecked;
public bool IsEnabled
{
get => isEnabled;
set
{
isChecked = value;
isEnabled = value;
RaisePropertyChanged(nameof(IsChecked));
RaisePropertyChanged(nameof(IsEnabled));
};
}
private bool isChecked;
}
From what I understand, you want to make an object bound to a "parent" (MenuItem => MultiplexerVM) be dependant on a property of its child collection (CheckBox => MultiplexVM.IsChecked, which is an item in MultiplexerVM.Multiplexes)
In this scenario, a child has to be somehow aware of its parent (when the child changes, it has to "push" the change up to the parent; in other words, the parent has to be informed when the change happens).
I can think of two ways to do it:
on the VM level: in every MultiplexVM, set a reference to the parent view model or collection, then you can update the CanCheckAll / CanUncheckAll functionality (however you implement it) every time the child's IsChecked changes (tedious; I suppose you can also do this with events, but attaching PropertyChanged handler to every child item is also a bit much)
cheat a bit by using the GUI level: you can update the CanCheckAll / CanUncheckAll functionality whenever the CheckBox is clicked
Below is an example of how you can implement the 2nd version.
In your MultiplexerVM:
public bool CanCheckAll => Multiplexes.Any(a => !a.IsChecked);
public bool CanUncheckAll => Multiplexes.Any(a => a.IsChecked);
public void RefreshCheckUncheckAll()
{
NotifyPropertyChanged(nameof(CanCheckAll));
NotifyPropertyChanged(nameof(CanUncheckAll));
}
Then, call RefreshCheckUncheckAll() in CheckAll command implementation and in:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
((MultiplexerVM)this.DataContext).RefreshCheckUncheckAll();
}
Then, the xaml will look something like this:
<ListView ItemsSource="{Binding Multiplexes}" SelectedItem="{Binding SelectedMultiplex}">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem
Command="{Binding CheckAll}"
CommandParameter="{StaticResource True}"
IsEnabled="{Binding CanCheck}">
<MenuItem.Header>
<TextBlock Text="Check all" />
</MenuItem.Header>
</MenuItem>
<MenuItem
Command="{Binding CheckAll}"
CommandParameter="{StaticResource False}"
IsEnabled="{Binding CanUncheck}">
<MenuItem.Header>
<TextBlock Text="Uncheck all" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="3" Text="{Binding Name}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="3" IsChecked="{Binding IsChecked}" Click="CheckBox_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>

Binding a property with a different ItemSource WPF C#

I have a list bound as ItemSource that contains two strings: Option 1 and option 2, I have 2 text boxes where I bind and display these two options. I also have two radio buttons next to the two textboxes. I want to bind these radiobuttons but every time I click on them nothing happens. I found out the reason for this, because now he is always trying to find the bool in my list whether the button is checked or not. Is there a way to set in the xaml code that I can access the bool property which is in my ViewModel?
ViewModel:
public class WortAuswahlViewModel : AntwortMoeglichkeitViewModel, IWortAuswahlViewModel
{
public ObservableCollection<AuswahlOptionen> m_auswahlOptionen;
public WortAuswahlViewModel(WortAuswahl wortAuswahl)
{
if (wortAuswahl?.Optionen == null)
{
return;
}
m_auswahlOptionen = new ObservableCollection<AuswahlOptionen>(wortAuswahl.Optionen);
}
public ObservableCollection<AuswahlOptionen> WortAuswahlOptionen
{
get
{
return m_auswahlOptionen;
}
set
{
if (m_auswahlOptionen != value)
{
m_auswahlOptionen = value;
OnPropertyChanged();
}
}
}
private bool m_isRadioButtonCheckedFirst;
public bool CheckButtonEins
{
get
{
return m_isRadioButtonCheckedFirst;
}
set
{
if (m_isRadioButtonCheckedFirst != value)
{
m_isRadioButtonCheckedFirst = value;
OnPropertyChanged();
}
}
}
private bool m_isRadioButtonCheckedSecond;
public bool CheckButtonZwei
{
get
{
return m_isRadioButtonCheckedSecond;
}
set
{
if (m_isRadioButtonCheckedSecond != value)
{
m_isRadioButtonCheckedSecond = value;
OnPropertyChanged();
}
}
}
}
}
XAML:
<Grid>
<StackPanel Grid.Row="1" Grid.Column="1" Margin="20">
<ItemsControl ItemsSource="{Binding WortAuswahlOptionen}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Viewbox Height="80" HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel>
<RadioButton HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" IsChecked="{Binding CheckButtonEins}"/>
<DockPanel LastChildFill="True">
<TextBox Grid.Column="1" Margin="20, 0, 0, 0" x:Name="TXT_optionEinsLoesung" Text="{Binding OptionEins}" IsReadOnly="True"/>
</DockPanel>
<RadioButton HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" IsChecked ="{Binding CheckeButtonZwei}"/>
<DockPanel LastChildFill="True">
<TextBox Grid.Column="1" Margin="20, 0, 0, 0" x:Name="TXT_optionZweiLoesung" Text="{Binding OptionZwei}" IsReadOnly="True"/>
</DockPanel>
</StackPanel>
</Viewbox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
DataContext of each ItemTemplate and ItemContainerStyle inside ItemsControl is automatically set to the corresponding element of the ItemsSource.
One way to redirect the DataContext to somewhere outside of the elements is to start the binding path from the DataContext of the root object of your Window.
So if your WortAuswahlViewModel is set to the DataContext of a Window, first you need to set the binding source to the Window using RelativeSource={RelativeSource AncestorType=Window} and then set path to Path=DataContext.CheckButtonEins
IsChecked="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.CheckButtonEins}"
If your WortAuswahlViewModel is set to the DataContext of another UI element, replace Window with the type of that element.

WPF CheckBox Command inside a Listview DataTemplate

My CheckBox Command does not work. I want to pass the SelectedItem of the ListView to the Command when the SelectedItem is checked or unchecked, but the Command does not execute at all. I also suspect my CommandParameter is not configured correctly?
I am pretty sure the problem is because the CheckBox is within a ListView DataTemplate.
Can someone show me how to set this up? I tried to follow examples I found, but nothing seems to work. thanks.
XAML
<ListView x:Name="lvReferralSource" ItemsSource="{Binding ReferralSourceTypeObsCollection}" Style="{StaticResource TypeListViewStyle}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<CheckBox x:Name="ckbReferralIsChecked" Content="{Binding Value}" IsChecked="{Binding Active}" Style="{StaticResource CheckBoxStyleBase2}"
Command="{Binding CheckBoxIsChecked}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=SelectedItem}">
</CheckBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CODE
private ICommand _CheckBoxIsChecked;
public ICommand CheckBoxIsChecked
{
get
{
if (_CheckBoxIsChecked == null)
{
_CheckBoxIsChecked = new RelayCommand<object>(ExecuteCheckBoxIsChecked, CanExecuteCheckBoxIsChecked);
}
return _CheckBoxIsChecked;
}
}
public bool CanExecuteCheckBoxIsChecked(object parameter)
{
return true;
}
public void ExecuteCheckBoxIsChecked(object parameter)
{
Mouse.OverrideCursor = Cursors.Wait;
if (parameter != null)
{
//Do Stuff...
}
Mouse.OverrideCursor = Cursors.Hand;
}
Your command should get executed provided that the CheckBoxIsChecked property belongs to the data object where the Value and Active properties are defined.
If it belongs to the view model, you could bind to it using a RelativeSource:
<CheckBox x:Name="ckbReferralIsChecked" Content="{Binding Value}" IsChecked="{Binding Active}"
Style="{StaticResource CheckBoxStyleBase2}"
Command="{Binding DataContext.CheckBoxIsChecked, RelativeSource={RelativeSource AncestorType=ListView}}"
CommandParameter="{Binding}">

How do I get the SelectedItem in a Listview with Listbox in it?

I am having trouble getting the selecteditem from a listbox which is a child of a listview. Everything that I've tried returns the GpoObject which is set at the parent listview, but not the selected OuLink from the Listbox.
This is my DataTemplate for the ListBox:
<DataTemplate x:Key="OuTemplate">
<Label Content="{Binding Path=Path}"/>
</DataTemplate>
This is my Listview with the ListBox in it:
<ListView x:Name="OutListView"
BorderBrush="#FFA0A0A0"
BorderThickness="1">
<ListView.View>
<GridView>
<GridViewColumn Header="Group Policy Objects"
Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Grid.Column="0"
Text="{Binding Path=Name}"
Width="Auto"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Organizational Units">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ListBox Grid.Column="1"
ItemsSource="{Binding Path=OUs}"
ItemTemplate="{DynamicResource OuTemplate}"
Width="Auto" Height="Auto"
BorderThickness="0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Object for binding:
public class GpoObject
{
public string Name {get; set;}
public string Id { get; set; }
public List<OuLink> OUs { get; set; }
}
public class OuLink
{
public string Path { get; set; }
}
Here are two ways to access the Path off of the ListBox selected item. I have named the listbox to make it easier in Xaml. To show the selected info I pathed to it in a textbox now which resides above the Listbox (see image):
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="tbSelected"
Text="{Binding ElementName=PathBox, Path=SelectedItem.Path}" />
<ListBox x:Name="PathBox"
SelectionChanged="PathBox_OnSelectionChanged"
ItemsSource="{Binding Path=OUs}"
ItemTemplate="{DynamicResource OuTemplate}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
Then on when the selection changes I present the user with a message box of the selected path:
private void PathBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var lbi = sender as ListBox;
if (lbi != null)
if (lbi.SelectedItem != null)
{
var link = lbi.SelectedItem as OuLink;
if (link != null)
MessageBox.Show(link.Path);
}
}
Here is a selection and its propagation to the textbox and the message box:
I suggest that within the OnSelectionChanged instead of a messagebox you place that selection into a INotifyPropertyChanged string property on your ViewModel and propagate it that way to other items within the program.
Add SelectedItem="{Binding SelectedOuLink}" to your ListBox in your xaml.
Then in your GpoObject class add:
public OuLink SelectedOuLink { get; set; }
You can now retrieve the selected OuLink object via SelectedOuLink.

BindingGroup in ListView with DataTemplate

I have recently found out about BindingGroups, and it seem rather nice.
But I can't get it to work in my ListView that is filled with data represented in DataTemplates.
(In advance I just like to say: sorry for all the code, it's just to give you a better understanding)
So in XAML it looks something like this:
<StackPanel Name="stpData">
<StackPanel.BindingGroup>
<BindingGroup />
</StackPanel.BindingGroup>
<!-- This works nice! -->
<TextBox Text="{Binding Path=name}" />
<CheckBox IsChecked="{Binding Path=enable}" />
<!-- This doesn't work so nice... -->
<ListView Name="lvBools" ItemsSource="{Binding Path=myList}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding bools}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
And code-behind:
public MainWindow()
{
InitializeComponent();
TestData Data = new TestData()
{
name = "Test",
enable = true,
myList = new List<TestData.myItem>
{
new TestData.myItem(true),
new TestData.myItem(true),
new TestData.myItem(false)
},
};
stpData.DataContext = Data;
stpData.BindingGroup.BeginEdit();
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
stpData.BindingGroup.CommitEdit();
stpData.BindingGroup.BeginEdit();
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
stpData.BindingGroup.CancelEdit();
stpData.BindingGroup.BeginEdit();
}
public class TestData
{
public class myItem
{
public myItem(bool b)
{
this.bools = b;
}
public bool bools { get; set; }
}
public string name { get; set; }
public bool enable { get; set; }
public List<myItem> myList { get; set; }
}
So what happends it that when I change the name, or press the enable CheckBox, those changes won't be submitted until I press the SaveButton (btnSave_Click) (not included in the XAML )
Or if I press a Cancel button changes is "restored".
But if I click one of the checkBoxes in the ListView, those changes will be submitted immediately.
My own guess: The ListView (or maybe even the StackPanel in the DataTemplate) stops the BindingGroup inheritance chain. But in that case, how can I include them?
EDIT
I have also tried to add a new BindingGroup to the DataTemplate:
<DataTemplate>
<StackPanel x:Name="SPpan">
<StackPanel.BindingGroup>
<BindingGroup Name="BGStackPanel" />
</StackPanel.BindingGroup>
<CheckBox IsChecked="{Binding Path=bools}" />
</StackPanel>
</DataTemplate>
Now when I edit the bools (Click them in my ListView ), the source doesn't get updated, and that's good, but now I can find no way to save (Commit) the data.
I can't access neither SPpan nor BGStackPanel from code-behind.
However I have tried the following:
var tmp = (lvBools.View as GridView).Columns[0];
(tmp.CellTemplate.LoadContent() as StackPanel).BindingGroup.BeginEdit();
but also this without success...
EDIT
Ok, so edit II... So I guess that the reason to why the source doesn't update is because I'm running the Begin/CommitEdit on the Template and not on the actual objects created from the template. So does anyone know how to reach those objects?
Ok, so thanks to Mr E.L Dunn I mannage to solve this one!
So in my first EDIT I suggested that I could maybe add a BindingGroup to the StackPanel in the DataTemplate, and that was kind of in the right direction...
All that's needed to be done is to add a BindingGroupName to the DataTemplate's CheckBox.
<StackPanel Name="stpData">
<StackPanel.BindingGroup>
<BindingGroup Name="bindingGroup"/>
</StackPanel.BindingGroup>
<!-- This works nice! -->
<TextBox Text="{Binding Path=name}" />
<CheckBox IsChecked="{Binding Path=enable}" />
<!-- Now this work nice as well! -->
<ListView Name="lvBools" ItemsSource="{Binding Path=myList}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding bools, BindingGroupName=bindingGroup}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
Simple as that!
You could place the various sub items in blocks of related radio buttons. Then put each group in a panel or a multiview. Then have the main radiolist do auto postback. On the postback read which radiobutton was clicked/selected then show the relevant sub group by revealing the relevant panel or view.
If you place these inside an updatepanel, you can do all of this without an obcious page refresh.

Categories

Resources