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.
Related
I have a little problem and I don't know how to fix it.
In my View I have a combobox, that should be able to display the property "Name" of the ObservableCollection "Phases". I already tried to show a single "Name-property" without a Datatemplate and it worked. I think I messed something up with the binding in the DataTemplate. What is wrong? Can you help me?
Here is my View:
<ComboBox ItemsSource="{Binding Phases}"
SelectedItem=""
Width="100" HorizontalAlignment="Left"
Margin="50,20">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Here my ObservableCollection:
public ObservableCollection<PhaseViewModel> Phases
{
get;
}
And here the property inside Phases:
public string Name
{
get { return myName; }
set { myName = value;}
}
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" />
Problem
I have a dynamic list of reports gathered from a database, and each of them will have the option to be exported in one of two formats, both, or neither. I would like this to be represented as a list of all reports with two adjacent checkboxes next to one one another (One checkbox for each report format) whose IsEnabled status will be predetermined.
My Current Solution
WPF
<Grid>
<TreeView Name="Views" Grid.Column ="0" ItemsSource="{Binding}" Margin="10,3,30,0" Width="230" MaxHeight="300" Height="244" VerticalAlignment="Top" BorderBrush="White">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="False" ></Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding OC}" >
<CheckBox IsChecked="{Binding CheckedFormat1}" IsEnabled="{Binding EnabledFormat1}" Click="CheckBoxStandardFormat1_Click" Loaded="CheckBoxStandardFormat1_Loaded" Margin="-20,0,0,0">
<CheckBox IsChecked="{Binding CheckedFormat2}" IsEnabled="{Binding EnabledFormat2}" Click="CheckBoxStandardFormat2_Click" Loaded="CheckBoxStandardFormat2_Loaded" >
<TextBlock Text="{Binding Name}" />
</CheckBox>
</CheckBox>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
OC is a data structure of reports which includes information on whether it is enabled for each report type. This code produced the following UI result:
Current Output
However it looks like each instance within the HeirachicalDataTemplate is inheriting the status of IsEnabled from the previous ones, as the greyscale changes.
Does anyone have a better solution which will allow me to bind a dynamic list of reports to a WPF output, with the formatting as described above?
Sorry in advance if this is an easy question, I am really new to C#
Update
The following is a sample of my ViewModel
[PropertyChanged.AddINotifyPropertyChangedInterface]
public class ReportsList
{
public string Name { get; set; }
public bool CheckedFormat1 { get; set; }
public bool CheckedFormat2 { get; set; }
public bool EnabledFormat1 { get; set; }
public bool EnabledFormat2 { get; set; }
public ReportsList()
{
EnabledFormat1 = true;
EnabledFormat2 = true;
}
}
and it's implementation as an ObservableCollection
public ObservableCollection<ReportsList> OC { get; set; }
There appears to be no need to use a TreeView here. You just have a list of reports. Replace TreeView with ListView or ItemsControl and replace HierarchicalDataTemplate with DataTemplate. Rather than nesting the CheckBoxes, use a StackPanel container to hold them together. And the margin of -20 wasn't helping. So you end up with:
<ItemsControl Name="Views" Grid.Column ="0" ItemsSource="{Binding OC}" Margin="10,3,30,0" Width="230" MaxHeight="300" Height="244" VerticalAlignment="Top" BorderBrush="White">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding CheckedFormat1}" IsEnabled="{Binding EnabledFormat1}" Click="CheckBoxStandardFormat1_Click" Loaded="CheckBoxStandardFormat1_Loaded" Margin="20,0,0,0"/>
<CheckBox IsChecked="{Binding CheckedFormat2}" IsEnabled="{Binding EnabledFormat2}" Click="CheckBoxStandardFormat2_Click" Loaded="CheckBoxStandardFormat2_Loaded" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
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.
I'm writing simple WPF Application and I wanted to use ListView to display List of items. My code is:
WPF.xaml
<ListView Grid.Column="0" Grid.Row="1" Margin="10,0,10,5" ItemsSource="{Binding MyCollection.Elements}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementDescriptions}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
WPF.xaml.cs
public MyViewModel ViewModel
{
get { return DataContext; }
set { DataContext = value; }
}
MyViewModel.cs
public OwnedCollection Elements { get; set; }
OwnedCollection.cs
public List<ElementDescriptions> ElementDescriptions { get; set; }
I'm 100% sure, that communication between View and ViewModel is correct, because displaying simple message doesn't make me troubles. Am I doing right binding in ListView?
A couple things:
First,
TextBlock Text="{Binding ElementDescriptions}"
doesn't make a lot of sense because ElementDescriptions is a collection. If you want to loop through all the ElementDescriptions in your List you should really be binding the ItemSource of the ListView to ElementDescriptions then accessing some text property of the ElementDescriptions class:
<ListView Grid.Column="0" Grid.Row="1" Margin="10,0,10,5" ItemsSource="{Binding MyCollection.ElementsElementDescriptions }">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementDescriptions.SomeTextField}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Second, are you using INotifyPropertyChanged so the view knows to update? More info on that here: OnPropertyChanged with a List