wpf - listview inside of tabcontrol - c#

I'm having difficulties to Binding a ListView inside of a TabControl
Bellow is my ViewModel
public ObservableCollection<TabItem> Tabs { get; set; }
public class TabItem
{
public string Header { get; set; }
public ObservableCollection<abc> Content { get; set; }
}
public class abc
{
public string text { get; set; }
}
this is how I initialize it
InitializeComponent();
Tabs = new ObservableCollection<TabItem>()
{
new TabItem {
Header = "Header1",
Content = new ObservableCollection<abc>()
{
new abc{ text = "content1" },
new abc{ text = "content2" },
}
},
new TabItem {
Header = "Header2",
Content = new ObservableCollection<abc>()
{
new abc{ text = "content3" },
new abc{ text = "content4" },
}
}
};
tabControl1.ItemsSource = Tabs;
and here is how I populate and Bind it in the view
<TabControl Name="tabControl1">
<TabControl.ItemTemplate>
<!-- this is the header template-->
<DataTemplate>
<TextBlock Width="500" Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- this is the body of the TabItem template-->
<DataTemplate>
<ListView Margin="10" ItemsSource="{Binding Content}">
<ListViewItem>
<StackPanel>
<TextBlock Text="{Binding text}" />
</StackPanel>
</ListViewItem>
</ListView>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
but then I got an errror saying that
InvalidOperationException: Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
How can I bind a ListView inside a TabControl?
Each Tab control will have the same format of ListView. The Actual ListView will have more than 1 field. not only text field.
Where did I do it wrongly?

ListViewItems are created by ListView for each element in ItemsSource. To change their look you need ItemTemplate:
<ListView Margin="10" ItemsSource="{Binding Content}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding text}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
if you have more than 1 field, not only text field, define GridView with columns like shown in this post

Related

Moving items in ItemsControl [duplicate]

This question already has answers here:
WPF 4 Drag and Drop with visual element as cursor
(2 answers)
Closed 1 year ago.
I have a list of items, which every item include a few of notes, like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Item> items = new List<Item>();
Item item1 = new Item() { Title = "First Item" };
Item item2 = new Item() { Title = "Second Item" };
item1.ItemNotes.Add(new ItemNote() { Note = "first_note" });
item1.ItemNotes.Add(new ItemNote() { Note = "second_note" });
item1.ItemNotes.Add(new ItemNote() { Note = "third_note" });
item2.ItemNotes.Add(new ItemNote() { Note = "fourth_note" });
item2.ItemNotes.Add(new ItemNote() { Note = "fifth_note" });
items.Add(item1);
items.Add(item2);
ic_tasks.ItemsSource = items;
}
}
public class Item
{
public Item()
{
this.ItemNotes = new ObservableCollection<ItemNote>();
}
public string Title { get; set; }
public ObservableCollection<ItemNote> ItemNotes { get; set; }
}
public class ItemNote
{
public string Note { get; set; }
}
In XAML I present those items by ItemsControl, like this:
<Grid>
<ItemsControl Name="ic_tasks">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:Item}">
<StackPanel Orientation="Vertical">
<Border BorderThickness="1" CornerRadius="8" Background="DarkGray">
<StackPanel Orientation="Horizontal" Height="30">
<TextBox Text="{Binding Title}" Margin="5,5,5,5"/>
<Button Margin="5,5,5,5">Fold</Button>
</StackPanel>
</Border>
<ItemsControl ItemsSource="{Binding ItemNotes}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ItemNote}">
<TextBox Margin="40,5,5,5" Text="{Binding Note}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
And now my goal is to make those Items moveable. For example, I would like to be able to move the second item over the first item, like below:
What I tried?
I try to wrap all ItemsControl by Canvas and bind position by Canvas.Top & Canvas.Left by using Mouse Events, but there's no such property for StackPanel which I want to move. There's only such propery for ItemsControl, like below:
<Canvas x:Name="canvas_of_items">
<ItemsControl Name="ic_tasks" Canvas.Top="{Binding YPosition}" Canvas.Left="{Binding XPosition}">
<...>
<StackPanel Orientation="Horizontal" Height="30" PreviewMouseDown="StackPanel_MouseDown" PreviewMouseMove="StackPanel_MouseMove" PreviewMouseUp="StackPanel_MouseUp">
<...>
</ItemsControl>
</Canvas>
So my question is:
What are the options to allow Items to be moved between themselves? Importantly, when moving the Item, it should be visible, not the mouse cursor itself.
You can set the ItemsControl to place your items in a canvas using ItemsControl.ItemsPanel like this:
<ItemsControl Name="ic_tasks">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas></Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
But to move the items you cant just set the Canvas properties on the StackPanel because they are surrounded by ContentPresenter. Instead you use ItemsControl.ItemContainerStyle to set a style on these ContentPresentors and there you can do something like this:
<Style>
<Setter Property="Canvas.Left" value="<<somthing - propebly a binding so it will be easy to change>>"
</Style>

In a TreeView in WPF, is there a way to get the index of the child node that is selected and the index of the parent node of that selected child item

I am trying to populate a treeview using a three-level object.
Input for the treeview is ObservableCollection(GroupClass) where GroupClass contains a property called devices = ObservableCollection(DeviceDetails).
When I click on a DeviceDetails object in the UI, I would like to get the index of DeviceDetails item and the index of the GroupClass object it belongs to.
I have successfully populated the treeview using the collection of the GroupClass using a hierarchical template and data template.
I have looked at how to get the details of the selected item in a treeview, but most of the solution used TreeViewItem.Selected trigger.
In my case, as I am using a hierarchical template and data template I don't have any TreeView Item defined in my XAML.
Group Class:
public class GroupClass
{
public GroupClass()
{
this.devices = new ObservableCollection<DeviceDetails>();
}
public string groupName { get; set; }
public ObservableCollection<DeviceDetails> devices { get; set; }
}
public class DeviceDetails
{
public DeviceDetails()
{
this.status = "Online";
this.ipAddress = "192.168.1.1";
this.dateAccessed = DateTime.Now;
}
public string deviceName { get; set; }
public string status { get; set; }
public DateTime dateAccessed { get; set; }
public string ipAddress { get; set; }
}
XAML code for populating TreeView:
<Grid x:Name="ListPage" HorizontalAlignment="Stretch" Margin="0,80,0,0" >
<TreeView x:Name="HostTreeView" HorizontalAlignment="Stretch" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:GroupClass}" ItemsSource="{Binding devices}">
<Grid HorizontalAlignment="Stretch" Name="HeaderPanel5">
<Grid Margin="0,0,0,0" >
<TextBlock Text="{Binding groupName}" />
</Grid>
</Grid>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:DeviceDetails}">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="checkBox" />
<TextBlock Text="{Binding deviceName}" />
<TextBlock Text="{Binding status}" />
<TextBlock Text="{Binding dateAccessed}" />
<TextBlock Text="{Binding ipAddress}" />
<Button Content="Rename Device" Height="30"/>
<Button Content="Remove Device" Height="30"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
I create a new object of type ObservableCollection and load the values into it from XML file. And then bind the created object to TreeView ItemsSource.
public static ObservableCollection<GroupClass> groups = new ObservableCollection<GroupClass>();
StreamReader sr = new StreamReader("IPProfileData");
XmlSerializer srlzr = new XmlSerializer(typeof(ObservableCollection<GroupClass>));
groups = (ObservableCollection<GroupClass>)srlzr.Deserialize(sr);
HostTreeView.ItemsSource = groups;
When I click on the Rename Device or Remove Device button, I would like to change the groups Object that is created earlier. Eventually my UI gets changed as TreeView is binded with groups object.

C# WPF ListView add icon near item

How add near item in listview on left side small icon?
WPF Code:
<ListView Name="listView1" x:FieldModifier="public" HorizontalAlignment="Left" Height="501" Margin="10,10,0,0" VerticalAlignment="Top" Width="312">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
Add item to listview
foreach (var item in found)
{
MainWindow.mainWindow.Dispatcher.Invoke(new Action(delegate ()
{
listView1.Items.Add(item);
}));
}
You just need to amend the DataTemplate as shown below.
POCO List View Item (Data Model):
public class MyListItem
{
public string Icon { get; set; }
public string Text { get; set; }
}
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var listItemObj = myListView.Items.Add(new MyListItem {Icon = "Default Icon", Text = "Default Text"});
}
}
XAML:
<ListView Name="myListView">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Icon}"/>
<TextBlock Text="{Binding Path=Text}" Padding="5 0 0 0"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Result:

WPF stackpanel does not show the whole content

I am using a Combobox to show the contents of two different Lists, the List<Production> productions and the List<Seasons> seasons. Below is the XAML code
<StackPanel Grid.Column=" 1" Grid.Row="2">
<ComboBox x:Name="comboBox" ItemsSource="{Binding ShowProductionsSeasonsNames}" SelectedItem="{Binding SelectedProductionsSeasons}">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="14"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
And this is the code in the viewmodel.
public ListCollectionView ShowProductionsSeasonsNames
{
get
{
List<ProductionsSeasonsNames> items = new List<ProductionsSeasonsNames>();
foreach (var production in this.Productions)
{
items.Add(new ProductionsSeasonsNames() { Name = production.DisplayName, Category = "Productions" });
}
foreach (var seasons in this.Seasons)
{
items.Add(new ProductionsSeasonsNames() { Name = seasons.DisplayName, Category = "Seasons" });
}
ListCollectionView lcv = new ListCollectionView(items);
lcv.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
return lcv;
}
}
public class ProductionsSeasonsNames
{
public string Name { get; set; }
public string Category { get; set; }
}
My issue is that it partly works. Although my productions and seasons lists contain around 100 items each, in the stackpanel only 15 from each list appear. Furthermore when I scroll down or up the focus goes directly to the top of each header without letting me to actually scroll (I am sorry I cant explain this part better). Does anyone have any ideas on this?
Use Grid instead of Stackpanel and set grid height="*" or you can directly put your combobox into Grid.Column=" 1" Grid.Row="2"

How can i use expander as a child item of treeview in WPF

I want to show a custom set of repeating datas
Class M
prop1 // treeview heading
List<String> // treeview items
List<ClassSub> [ prop 1, porp2 ,List<String> sub items] //as an expander with List<String> as expander items
Class M
prop1 // treeview heading
prop2 // treeview items
List<ClassSub>[prop1 ,prop2,List<String>...]//as an expander
Can i use a Treeview + Expander combination to arrange this complete set of data?
Or do i need to use a Codeplex librarys like http://complexdatatemplates.codeplex.com [But i dont see a clear documentation on it]
here is how I offer to solve your issue
xaml
<TreeView ItemsSource="{Binding TreeItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Expander Header="{Binding Header}">
<ItemsControl Margin="25,0,0,0" ItemsSource="{Binding TreeItems}" />
</Expander>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Item class
class Item
{
public string Header { get; set; }
public List<string> TreeItems { get; set; }
public List<Item> SubItems { get; set; }
}
View Model
class ViewModel
{
public ViewModel()
{
TreeItems = new ObservableCollection<Item>();
string[] data = new string[]{
"Subchapter 1","Subchapter 2",
};
Item item = new Item()
{
Header = "Sub Getting Started",
TreeItems = new List<string>(data)
};
TreeItems.Add(new Item()
{
Header = "Getting Started 1",
SubItems = new List<Item>(new Item[] { item }),
TreeItems = new List<string>(data)
});
TreeItems.Add(new Item()
{
Header = "Getting Started 2",
SubItems = new List<Item>(new Item[] { item }),
TreeItems = new List<string>(data)
});
TreeItems.Add(new Item()
{
Header = "Getting Started 3",
SubItems = new List<Item>(new Item[] { item }),
TreeItems = new List<string>(data)
});
}
public ObservableCollection<Item> TreeItems { get; private set; }
}
result
In example above I haven't styled the Expander to match the one in screenshot posted in question. let me know if that is also required.
EDIT
as discussed, to display SubChapter1.1. and 1.2 to be visible by default or expand any other tree view item by default
start by defining a style for TreeViewItem in the resources
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded"
Value="{Binding IsExpanded}" />
</Style>
</TreeView.Resources>
add a property IsExpanded to BookChapter class
public class BookChapter
{
public string name { get; set; }
public string id { get; set; }
public List<BookPage> pages { get; set; }
//public List<Enrichment> enrichment { get; set; }
public List<SubChapter> chapters { get; set; }
public bool IsExpanded { get; set; }
}
then set IsExpanded to true if you want it to be initially expanded
BookChapter cha = new BookChapter { name = "Chapter Intro", pages = pags, IsExpanded=true };
result
you can control other tree items in a similar manner too.
EDIT 2
sample for the same using Listbox and data templates
<Grid>
<Grid.Resources>
<DataTemplate x:Key="chapterTemplate">
<Expander Header="{Binding name}" IsExpanded="{Binding IsExpanded}">
<StackPanel>
<ItemsControl Margin="25,0,0,0"
ItemsSource="{Binding pages}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Path=label}">
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListBox ItemsSource="{Binding chapters}"
BorderBrush="{x:Null}"
Margin="20,0,0,0" />
</StackPanel>
</Expander>
</DataTemplate>
<DataTemplate DataType="{x:Type l:BookChapter}">
<ContentControl Content="{Binding}"
ContentTemplate="{StaticResource chapterTemplate}" />
</DataTemplate>
<DataTemplate DataType="{x:Type l:SubChapter}">
<ContentControl Content="{Binding}"
ContentTemplate="{StaticResource chapterTemplate}" />
</DataTemplate>
<DataTemplate DataType="{x:Type l:BookPage}">
<Button Content="{Binding Path=label}" />
</DataTemplate>
</Grid.Resources>
<ListBox ItemsSource="{Binding}"
Name="TOCView" />
</Grid>
result
with this approach you may not need to template the tree view item for the arrow. just template the expander as you wish
as a suggestion you can reuse BookChapter class as subchapters instead of creating a new class for SubChapter unless necessary.

Categories