How to create a three level vertical menu in WPF? - wpf

I am trying to create a Side menu in wpf as shown in the image...
The menu is binded to an ObservableCollection (MenuList) of the class
public class MenuItemModel
{
public int MenuID { get; set; }
public string MenuName { get; set; }
public ObservableCollection<MenuItemModel> SubMenuList { get; set; }
}
The Xaml I have been using for two level,
<ItemsControl ItemsSource="{Binding MenuList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<RadioButton x:Name="Menu"
Tag="{Binding MenuID}"
GroupName="MainMenu"
Style="{StaticResource MenuButtonStyle}"
Content="{Binding MenuName}"
Height="30"/>
<ListBox x:Name="SubMenu" ItemsSource="{Binding SubMenuList}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It works perfectly for two levels. But I am unable to figure out how to implement third level menus (like version1, version2 in the image)? Is there any way to do this in the xaml without changing Data Models?

Related

UWP XAML How do I wrap text in a bound ListView

How do I wrap or otherwise display long strings in my listview control. I have been unsuccessful in wrapping, or otherwise displaying long text in my bound ListView control. My xaml page is basically a BOUND FlipView with an ItemTemplate that contains two bound textBlocks and a bound ListView. I can get the TextBlocks to wrap but not the listviewitems. It would seem like such a simple thing yet it eludes me.
Here is a portion of my xaml:
<Page.Resources>
<DataTemplate x:DataType="data:MydataObject" x:Key="MydataObjectTemplate">
<StackPanel HorizontalAlignment="Stretch" Height="596" Width="982">
<TextBlock Name="txtDataObjectId" Text="{Binding dataObject.Id}" Visibility="Collapsed" TextWrapping="WrapWholeWords"/>
<TextBlock FontSize="24" Text="{x:Bind dataObject}" HorizontalAlignment="Center" TextWrapping="WrapWholeWords"/>
<ListView ItemsSource ="{x:Bind theObjectDetails, Mode=OneWay }"
HorizontalAlignment="Stretch"
BorderBrush="Black"
BorderThickness="1"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel HorizontalAlignment="Stretch">
<ComboBox x:Name="cboCategory" Header="Category" SelectionChanged="cboCategory_SelectionChanged" />
<FlipView x:Name="FlipView1"
ItemsSource="{x:Bind MydataObjects, Mode=OneWay }"
ItemTemplate="{StaticResource MydataObjectTemplate}"
BorderBrush="Black"
BorderThickness="1"/>
</StackPanel>
</Grid>
//c#
public class mydataObject
{
public int Id { get; set; }
public dataObject theObject { get; set; }
public List<dataObjectDetails> theObjectDetails { get; set; }
public override string ToString()
{
return this.theObject.Subject;
}
}
public class dataObjectDetails
{
public int Id { get; set; }
public int dodId{ get; set; }
public string bodyText { get; set; }
public override string ToString()
{
return bodyText ;
}
}
Give the ListView an ItemTemplate, which puts the content in a TextBlock that wraps the text:
<ListView
ItemsSource="{x:Bind theObjectDetails, Mode=OneWay}"
HorizontalAlignment="Stretch"
>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock
Text="{Binding bodyText}"
TextWrapping="WrapWholeWords"
/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Wpf adding stack panels from view

I'm not sure my title is clear (poor wpf skills).
What i'm trying to do is to create a smart data entry form. My goal is to have a hard coded data that the user should enter, and on demand (a plus button) he can enter another set of data, every time the user will click the plus button another set will appear in the window (endless)
Edit:
For more details, for a very simple example of what i'm trying to achieve, lets say that this is the window:
And after the user will click the plus button the window will look like this:
And the plus button will always let the user adding more peoples.
Seems like all you need is a List and a ItemControl:
Your Model:
public class User
{
public String Name { get; set; }
public int Age { get; set; }
}
In your ViewModel:
public List<User> Users { get; set; }
//In your constructor
Users = new List<User>();
In your View:
<ItemsControl ItemsSource={Binding Users}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="Name:" />
<TextBox Text="{Binding Name}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="Age:" />
<TextBox Text="{Binding Age}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And then below this wire up your add button to a command to point to a method that would do someething like:
private void AddUser()
{
Users.Add(new User());
NotifyPropertyChange("Users");
}
Use an ItemsControl with its ItemsSource property bound to a ReadOnlyObservableCollection<Person>, where Person is a class holding the name and age as strings.
(1) Create Person
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
(2) Create PeopleViewModel, holding your collection.
public class PeopleViewModel
{
private ObservableCollection<Person> _people;
public ReadOnlyObservableCollection<Person> People { get; private set; }
public PeopleViewModel()
{
_people = new ObservableCollection<Person>();
People = new ReadOnlyObservableCollection<Person>(_people);
addPerson(); // adding the 1st person
}
// You also need to hook this up to the button press somehow
private void addPerson()
{
_people.Add(new Person());
}
}
(3) Set the DataContext of your window to be a PersonViewModel in the code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new PeopleViewModel();
}
}
(4) Create an ItemsControl along with a DataTemplate for Person
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="name:" />
<TextBox Text="{Binding Name}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="age:" />
<TextBox Text="{Binding Age}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Don't forget to hook up your button either through a Command or through the Button.Click event.

WPF TabControl ContentTemplate List

Inexperienced with WPF, so I need a little help. Appreciate the help in advance.
I have the following class:
public class TabDefn
{
public TabDefn() { }
public TabDefn(string inFolderName, List<FilesFolder> inFilesFolders)
{
folderName = inFolderName;
FFs = inFilesFolders;
}
public string folderName { get; set; }
public List<FilesFolder> FFs {get; set;}
}
public class FilesFolder
{
public FilesFolder() {}
//public Image image { get; set; }
public string ffName { get; set; }
//public Image arrow { get; set; }
}
The TabControl.ItemContent is working fine. I can't get anything to show up for the TabControl.ContentTemplate. I've tried many things, but this is where the WPF is right now:
<TabControl Grid.Column="1" Grid.Row="1" Visibility="Hidden" Name="Actions">
<!-- This displays the tab heading perfectly.-->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding folderName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<!-- This is the content of the tab that I can't get anything to show up in.-->
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding FF}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ffName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I don't care if the content changes, so I don't need the INotifyPropertyChanged or ObservableCollection. However, if I have to put all that code in, so be it.
You declare FF as field which is invalid binding source. You need to convert it into property
public List<FilesFolders> FF { get; set; }
and initialize it for example in TabDefn constructor. You can find more as to what is a valid binding source on MSDN

Rendering Collection of collections - ItemsControl

I have an object model like below:
public class ViewModel
{
public List<Group> Groups{ get; set; }
}
public class Group
{
public string Name { get; set; }
public List<Contact> Contacts { get; set; }
}
public class Contact
{
public string Name { get; set; }
public bool IsOnline { get; set; }
}
and I'm binding the groups to an itemscontrol like this:
<ItemsControl ItemsSource="{Binding Path=Groups}"
ItemTemplate="{StaticResource GroupTemplate}" >
</ItemsControl>
and I have datatemplate for rendering them.
<DataTemplate x:Key="GroupTemplate" DataType="{x:Type Group}">
</DataTemplate>
<DataTemplate x:Key="ContactTemplate" DataType="{x:Type Contact}">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
</StackPanle>
</DataTemplate>
How can I get the contacts displayed inside the items control? The contacts is a collection inside each group and my viewmodel has a collection of groups. To complicate it a bit further, I have different datatemplates for different contacts, and I should be using a datatemplateselector for choosing the appropriate contact template. Also please note, I have nothing to display in the group template, and I only need to show Contacts.
Thanks,
-Mike
Use another ItemsControl in the first template:
<DataTemplate x:Key="GroupTemplate" DataType="{x:Type my:Group}">
<ItemsControl ItemsSource="{Binding Contacts}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type my:Contact}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
And with a template selector:
<DataTemplate x:Key="GroupTemplate" DataType="{x:Type my:Group}">
<ItemsControl ItemsSource="{Binding Contacts}"
ItemTemplateSelector="{StaticResource yourContactItemSelector}"/>
</DataTemplate>

Multi level Nested TreeView with Dynamic Binding in WPF

I am trying to create an application in which i require to display employees and their departments in the treeview kind of structure as below :
Employee1
Department
Dept1
Dept2
Employee2
Department
Dept3
Dept4
how could i do this with WPF ?
The correct way to do this is to use a HierarchicalDataTemplate. The most basic one I can imagine is the following:
<UserControl.Resources>
<HierarchicalDataTemplate
x:Key="RecursiveData" DataType="TreeViewItem" ItemsSource="{Binding Items}">
</HierarchicalDataTemplate>
</UserControl.Resources>
Which can be used in the XAML as follows:
<TreeView ItemTemplate="{StaticResource RecursiveData}" />
Of course you can customize the template at will with styles and subcomponents.
Note that the ItemSource of your TreeView needs to actually provide nested TreeViewItems where each TreeViewItem contains it's subitems in Items.
If you've structure like this:
public ObservableCollection<ChartOfAccount> ChartOfAccounts { get; set; }
public class ChartOfAccount
{
public Book Book { get; set; }
public List<LedgerHierarchy> ControlLedgers { get; set; }
}
public class LedgerHierarchy
{
public ControlLedger ControlLedger { get; set; }
public ObservableCollection<Ledger> Ledgers { get; set; }
}
you could bind directly in TreeView like this:
<TreeView ItemsSource="{Binding ChartOfAccounts}"
BorderThickness="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemContainerStyle="{StaticResource treeStyle}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ControlLedgers}">
<TextBlock Text="{Binding Book.Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Ledgers}">
<TextBlock Text="{Binding ControlLedger.Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
instead of creating HierarchicalDataTemplate in Control.Resource.

Resources