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>
Related
Hy Everyone!
My situation so far:
I have an observable collection ("PipesSystemList") which is made up of "DataBaseSystem" objects:
public static ObservableCollection<DatabaseSystem> PipesSystemList { get; set; } = new ObservableCollection<DatabaseSystem>();
public class DatabaseSystem
{
public string System { get; set; }
public bool IsChecked { get;set;}
}
I successfully bind this collection to my combobox as it follows and everything works perfectly:
<ComboBox ItemsSource="{Binding ElementName=MainView,Path=DataContext.PipesSystemList}"
Padding="2" Margin="5 0 5 0" Width="70" Height="20"
Foreground ="#F1F1F1" Style="{StaticResource ComboBoxStyleDark}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="local:DatabaseSystem">
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding System}" Foreground="#F1F1F1"
IsChecked="{Binding IsChecked}"
Command="{Binding ElementName=MainView, Path=DataContext.PipeTabChosenSystemChangedCommand }"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
What I wanted to achieve: Because I have multiple equipment type, I wanted to have all the necesarry collection in one place. so I created a dictionary:
var SystemTabLists = new Dictionary<string, ObservableCollection<DatabaseSystem>>()
{
{"Document List",new ObservableCollection<DatabaseSystem>()},
{"Pipes",new ObservableCollection<DatabaseSystem>()},
{"Valves",new ObservableCollection<DatabaseSystem>()},
{"Sensors",new ObservableCollection<DatabaseSystem>()},
{"Vessels",new ObservableCollection<DatabaseSystem>()},
{"Heat Exchangers",new ObservableCollection<DatabaseSystem>()},
{"Filters",new ObservableCollection<DatabaseSystem>()},
{"Other Equipment",new ObservableCollection<DatabaseSystem>()}
};
After this, I tried to bind the combobox to the collection belonging to the Pipes (I successfully managed to fill up the collection with the right elements, so the data I need is there):
<ComboBox ItemsSource="{Binding ElementName=MainView,Path=DataContext.SystemTabLists[Pipes]}"
Padding="2" Margin="5 0 5 0" Width="70" Height="20"
Foreground ="#F1F1F1" Style="{StaticResource ComboBoxStyleDark}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="local:DatabaseSystem">
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding System}" Foreground="#F1F1F1"
IsChecked="{Binding IsChecked}"
Command="{Binding ElementName=MainView, Path=DataContext.PipeTabChosenSystemChangedCommand }"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Unfortunately this is not working.
When I debug the code and I turn on tracing for bindings I receive this:
"BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=DataContext.SystemTabLists[Pipes]"
Could someone help me what am I doing wrong? Or point out if this is not possible to do.
Thanks in advance.
I need to pass values from view to view model still preserving mvvm. Let me explain the issue.
<UserControl x:Class="SelectorView" ...>
<ListView Name="gridListView" ItemsSource="{Binding... }">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="false"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<StackPanel Orientation="Vertical">
<Label x:Name="vLabel" Content="{Binding VCValue}"/>
<ListView Name="checkBoxListView" ItemsSource="{Binding CList}">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Command="{Binding DataContext.OnCheckedCommand,RelativeSource={RelativeSource Mode=Self}}" Margin="5" Click="CheckBox_Click" IsChecked="{Binding SelectedValue, Mode=TwoWay}" Content="{Binding Current, Mode=OneWay }"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
In above code, When user checks/unchecks the checkbox, i need to send value(content) of checkbox and also value of vLabel from view to view model. Both are within nested Data template. Earlier I used visualtreehelper to retrieve the same but I need to do this more in mvvm format. Hence, want to know if its doable using Command binding and how ? In check box, i binded to OnCheckedCommand but that doesn't seem to invoke OnCheckedExecuted() on check/uncheck.
So, how do i get the content of checkbox and vLabel on check/uncheck?
public class MainViewModel
{
public DelegateCommand<object> OnCheckedCommand { get; private set; }
public MainViewModel()
{
OnCheckedCommand = new DelegateCommand<object>(OnCheckedExecuted, CanCheck);
}
private void OnCheckedExecuted(object parameter)
{
}
private bool CanCheck(object parameter)
{
return true;
}
}
Any help is appreciated!
So I'm trying to do a program that uses ListView (instead of CheckedListBox), and I want to get the indexes of the checked elements.
I use this as a template for the ListView:
<ListView x:Name="Content" HorizontalAlignment="Left" Height="303" Margin="10,47,0,0" VerticalAlignment="Top" Width="394" ItemClick="Content_ItemClick" SelectionChanged="Content_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Selected}"></CheckBox>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And this is the data what it got:
public class User
{
public string Name { get; set; }
public bool Selected { get; set; }
}
How could I get back the checked elements index?
I believe your ListView's item source is bound to a backing list
<ListView ItemSource"{x:Bind UserList, Mode=OneWay}" >
<ListItem.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Selected, Mode=TwoWay} Click="Click_Handler"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListItem.ItemTemplate>
</ListView>
The UserList is List<User> which contains all your User.
Since you bind the UserList to the ListView. You could retrieve the selected item by iterating through the list and read the Selected property of each individual User.
I have following XAML:
<ItemsControl x:Name="TextComboPairItemsControl" Grid.Row="1" Grid.ColumnSpan="2"
ItemsSource="{Binding Path=AllHeaders.Fields}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock x:Name="TextBlock1" Text="{Binding}"
Grid.Column="0" Margin ="2"/>
<ComboBox x:Name="ComboBox1" ItemsSource="{Binding ElementName=MainGrid, Path=DataContext.Tags}"
SelectedItem="{Binding ElementName=MainGrid, Path=DataContext.TextComboPairList.Combo}"
Grid.Column="1" Margin ="2" SelectedIndex="0" IsEditable="True"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now, in my code I want to be able to read what the user has chosen in each ComboBox. For that I created a class:
public class TextComboPair
{
public string TextContent { get; set; }
public string ComboContent { get; set; }
}
Every pair of TextBlock and ComboBox would have its own object of the above class.
I also created a list to store all those pairs of data:
public List<TextComboPair> TextComboPairList
{
get;
set;
}
It is defined in my DataContext.
So, if, for example, there was displayed a list of three TextBlock-ComboBox pairs on the screen and user would choose what he needs in each ComboBox, I'd like to have the above List populated with that data.
As you can see in XAML I bound Selected Item to this List, but I must have done it wrong.
Hwo can I fix this?
Try this :
<ComboBox x:Name="ComboBox1" ItemsSource="{Binding Path=DataContext.Tags, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl }}}"
SelectedItem="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl }}, Path=DataContext.TextComboPairList.Combo}"
Grid.Column="1" Margin ="2" SelectedIndex="0" IsEditable="True"/>
Say I have the following class, Employee
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public ObservableCollection<Employee> Underlings { get; set; }
}
And then I have the following XAML, bound to an ObservableCollection<Employee> MyEmployees
<ListBox ItemsSource="{Binding Path=MyEmployees}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Employee.Id}">
<TextBlock Text="{Binding Path=Employee.Name}"></TextBlock>
<!-- Here's where I declare my underlings -->
<ListBox ItemsSource="{Binding Path=Employee.Underlings}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Employee.Id}">
<TextBlock Text="{Binding Path=Employee.Name}"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This allows each employee in the collection MyEmployees to have some underlings. But those underlings are also of type employee, and could have their own underlings. How do I cater for those additional levels without making my XAML very complex?
Is there some way to declare my DataTemplate separately and allow it to be referenced within itself?
Do I have to do all this from code-behind instead?
(I realise the XAML above may not be 100% correct, its just an example)
therefore you have to use a TreeView and not a ListBox. And You have to specify a HierarchicalDataTemplate.
You could define the DataTemplate inside the ListBoxes Resources as the default template for Employees:
<ListBox ItemsSource="{Binding MyEmployees}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type myns:Employee}">
<Grid Tag="{Binding Id}">
<TextBlock Text="{Binding Name}"></TextBlock>
<ListBox ItemsSource="{Binding Underlings}" />
</Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>