Semantic zoom - tap item to navigate to specific page - c#

How can I get my app to navigate to a specific page based on the item within the semantic zoom that was tapped? Each item has a link to its own page and I want to use the 'item.Link' element so that the app reads the link and uses it to navigate to the specified page.
MetropolitanDataSource.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exits_Expert_London_Lite
{
using System;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
// To significantly reduce the sample data footprint in your production application, you can set
// the DISABLE_SAMPLE_DATA conditional compilation constant and disable sample data at runtime.
#if DISABLE_SAMPLE_DATA
internal class SampleDataSource { }
#else
public class Item : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
private string _Station = string.Empty;
public string Station
{
get
{
return this._Station;
}
set
{
if (this._Station != value)
{
this._Station = value;
this.OnPropertyChanged("Station");
}
}
}
private string _Zone = string.Empty;
public string Zone
{
get
{
return this._Zone;
}
set
{
if (this._Zone != value)
{
this._Zone = value;
this.OnPropertyChanged("Zone");
}
}
}
private string _Link = string.Empty;
public string Link
{
get
{
return this._Link;
}
set
{
if (this._Link != value)
{
this._Link = value;
this.OnPropertyChanged("Link");
}
}
}
}
public class GroupInfoList<T> : List<object>
{
public object Key { get; set; }
public new IEnumerator<object> GetEnumerator()
{
return (System.Collections.Generic.IEnumerator<object>)base.GetEnumerator();
}
}
public class StoreData
{
public StoreData()
{
Item item;
item = new Item();
item.Station = "Amersham";
item.Zone = "Fare zone 9";
item.Link = "/Lines and Stations/Metropolitan/AMR_(Metropolitan).xaml";
Collection.Add(item);
item = new Item();
item.Station = "Chalfont & Latimer";
item.Zone = "Fare zone 8";
item.Link = "/Lines and Stations/Metropolitan/CFO_(Metropolitan).xaml";
Collection.Add(item);
item = new Item();
item.Station = "Chesham";
item.Zone = "Fare zone 9";
item.Link = "/Lines and Stations/Metropolitan/Chesham.xaml";
Collection.Add(item);
}
private ItemCollection _Collection = new ItemCollection();
public ItemCollection Collection
{
get
{
return this._Collection;
}
}
internal List<GroupInfoList<object>> GetGroupsByCategory()
{
List<GroupInfoList<object>> groups = new List<GroupInfoList<object>>();
var query = from item in Collection
orderby ((Item)item).Zone
group item by ((Item)item).Zone into g
select new { GroupName = g.Key, Items = g };
foreach (var g in query)
{
GroupInfoList<object> info = new GroupInfoList<object>();
info.Key = g.GroupName;
foreach (var item in g.Items)
{
info.Add(item);
}
groups.Add(info);
}
return groups;
}
internal List<GroupInfoList<object>> GetGroupsByLetter()
{
List<GroupInfoList<object>> groups = new List<GroupInfoList<object>>();
var query = from item in Collection
orderby ((Item)item).Station
group item by ((Item)item).Station[0] into g
select new { GroupName = g.Key, Items = g };
foreach (var g in query)
{
GroupInfoList<object> info = new GroupInfoList<object>();
info.Key = g.GroupName;
foreach (var item in g.Items)
{
info.Add(item);
}
groups.Add(info);
}
return groups;
}
}
// Workaround: data binding works best with an enumeration of objects that does not implement IList
public class ItemCollection : IEnumerable<Object>
{
private System.Collections.ObjectModel.ObservableCollection<Item> itemCollection = new System.Collections.ObjectModel.ObservableCollection<Item>();
public IEnumerator<Object> GetEnumerator()
{
return itemCollection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(Item item)
{
itemCollection.Add(item);
}
}
#endif
}
Metropolitan_line.xaml.cs
using Exits_Expert_London_Lite.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237
namespace Exits_Expert_London_Lite.Lines_and_Stations.Metropolitan
{
public sealed partial class Metropolitan_line : Page
{
public Metropolitan_line()
{
this.InitializeComponent();
StoreData _storeData = null;
// creates a new instance of the sample data
_storeData = new StoreData();
// sets the list of categories to the groups from the sample data
List<GroupInfoList<object>> dataLetter = _storeData.GetGroupsByLetter();
// sets the CollectionViewSource in the XAML page resources to the data groups
cvsMetropolitan.Source = dataLetter;
// sets the items source for the zoomed out view to the group data as well
(semanticZoom.ZoomedOutView as ListViewBase).ItemsSource = cvsMetropolitan.View.CollectionGroups;
(semanticZoom.ZoomedInView as ListViewBase).SelectedIndex = -1;
}
#region Data Visualization
void ItemsGridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
ItemViewer iv = args.ItemContainer.ContentTemplateRoot as ItemViewer;
if (args.InRecycleQueue == true)
{
iv.ClearData();
}
else if (args.Phase == 0)
{
iv.ShowPlaceholder(args.Item as Item);
args.RegisterUpdateCallback(ContainerContentChangingDelegate);
}
else if (args.Phase == 1)
{
iv.ShowStation();
args.RegisterUpdateCallback(ContainerContentChangingDelegate);
}
else if (args.Phase == 2)
{
iv.ShowZone();
}
args.Handled = true;
}
private TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs> ContainerContentChangingDelegate
{
get
{
if (_delegate == null)
{
_delegate = new TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs>(ItemsGridView_ContainerContentChanging);
}
return _delegate;
}
}
private TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs> _delegate;
#endregion //Data Visualization
private void backButton_Tapped(object sender, TappedRoutedEventArgs e)
{
this.Frame.Navigate(typeof(Station_Chooser));
}
void GridView_ItemClicked(object sender, ItemClickEventArgs args)
{
var item = (Item)args.ClickedItem;
if (item.Link == "/Lines and Stations/Metropolitan/AMR_(Metropolitan).xaml")
((Frame)Window.Current.Content).Navigate(typeof(AMR__Metropolitan_));
else if (item.Link == "/Lines and Stations/Metropolitan/CFO_(Metropolitan).xaml")
((Frame)Window.Current.Content).Navigate(typeof(CFO__Metropolitan_));
else if (item.Link == "/Lines and Stations/Metropolitan/Chesham.xaml")
((Frame)Window.Current.Content).Navigate(typeof(Chesham));
}
}
}
ItemViewer.xaml
<UserControl
x:Class="Exits_Expert_London_Lite.ItemViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Exits_Expert_London_Lite"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Margin="0,0,0,20" HorizontalAlignment="Left">
<StackPanel>
<StackPanel Margin="10,0,0,0" Width="420">
<TextBlock x:Name="stationTextBlock" Foreground="{StaticResource ApplicationForegroundThemeBrush}" VerticalAlignment="Center" HorizontalAlignment="Left" FontFamily="Segoe UI" FontSize="32" FontWeight="Light" />
<TextBlock x:Name="zoneTextBlock" TextWrapping="Wrap" Foreground="White" FontSize="20" FontWeight="Light" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
Metropolitan_line.xaml
<Page
x:Class="Exits_Expert_London_Lite.Lines_and_Stations.Metropolitan.Metropolitan_line"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Exits_Expert_London_Lite"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="cvsMetropolitan" IsSourceGrouped="true" />
</Page.Resources>
<Grid Background="Black">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button" Tapped="backButton_Tapped"/>
<TextBlock x:Name="pageTitle" Text="Metropolitan line" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40" Foreground="#FF9B0056"/>
</Grid>
<Grid x:Name="Output" Grid.Row="1">
<!-- This shows a hard-coded width to show within the SDK Sample framework. In a real application you likely wouldn't set a width on the SemanticZoom -->
<SemanticZoom x:Name="semanticZoom" Margin="0,0,0,0">
<SemanticZoom.ZoomedOutView>
<GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False" >
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Group.Key}" HorizontalAlignment="Center" FontFamily="Segoe UI" FontWeight="Light" FontSize="56" Foreground="White"/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid ItemWidth="200" ItemHeight="200" MaximumRowsOrColumns="4" VerticalAlignment="Center"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="4" />
<Setter Property="Padding" Value="10" />
<Setter Property="Background" Value="#9B0056" />
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
</GridView.ItemContainerStyle>
</GridView>
</SemanticZoom.ZoomedOutView>
<SemanticZoom.ZoomedInView>
<GridView x:Name="ItemsGridView"
ItemsSource="{Binding Source={StaticResource cvsMetropolitan}}"
ShowsScrollingPlaceholders="False"
ContainerContentChanging="ItemsGridView_ContainerContentChanging"
IsSwipeEnabled="True" ScrollViewer.IsHorizontalScrollChainingEnabled="False" ItemClick="GridView_ItemClicked">
<GridView.ItemTemplate>
<DataTemplate>
<local:ItemViewer/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text='{Binding Key}' Foreground="#FF9B0056" Margin="5" FontSize="50" FontFamily="Segoe UI" FontWeight="Light" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
</SemanticZoom.ZoomedInView>
</SemanticZoom>
</Grid>
</Grid>
</Page>

Here you go. In your code-behind...
void GridView_ItemClicked(object sender, ItemClickEventArgs args)
{
var item = (Item)args.ClickedItem;
if (item.Link == "/Page_1.xaml")
((Frame)Window.Current.Content).Navigate(typeof (Page_1));
else if (item.Link == "/Page_2.xaml")
((Frame)Window.Current.Content).Navigate(typeof (Page_2));
else if (item.Link == "/Page_3.xaml")
((Frame)Window.Current.Content).Navigate(typeof (Page_3));
}
Best of luck!

Related

Using Update button in WPF ListView to update Selected Item

I have to make a WPF App to add, delete and update Items for Client Management purposes. I have so far been able to add and remove the items to the List using MVVM but I am unable to update the selected items. I require help in this regard.
The code for the project is as follows:
Details Class.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assignment_2.Model
{
public class Details :INotifyPropertyChanged
{
private string name;
public string Name
{ get { return name; } set { name = value; OnPropertyChanged(Name); } }
private string company;
public string Company
{ get { return company; } set { company = value; OnPropertyChanged(Company); } }
private string region;
public string Region
{ get { return region; } set { region = value; OnPropertyChanged(Region); } }
private string hra;
public string HRA
{
get { return hra; }
set
{
hra = value;
OnPropertyChanged(HRA);
}
}
private string basic;
public string Basic
{ get { return basic; }
set { basic = value;
OnPropertyChanged(Basic);
}
}
private string pf;
public string PF
{ get { return pf; }
set
{
pf = value;
OnPropertyChanged(PF);
}
}
private string salary;
public string Salary
{ get { return salary; }
set
{
salary = value;
OnPropertyChanged(Salary);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string det)
{
PropertyChangedEventHandler pc = PropertyChanged;
if (pc != null)
pc(this, new PropertyChangedEventArgs(det));
}
}
}
DetailsViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Assignment_2.Commands;
using Assignment_2.Model;
namespace Assignment_2.ViewModel
{
public class DetailsViewModel : INotifyPropertyChanged
{
private Details record;
public Details Record
{
get { return record; }
set { record = value; NotifyPropertyChanged("Record"); }
}
private ObservableCollection<Details> records;
public ObservableCollection<Details> Records
{
get { return records; }
set { records = value; } // NotifyPropertyChanged("Records");
}
private ICommand _SubmitCommand;
public ICommand SubmitCommand
{
get
{
if (_SubmitCommand == null)
{
_SubmitCommand = new RelayCommand(SubmitExecute, CanSubmitExecute, false);
}
return _SubmitCommand;
}
}
public DetailsViewModel()
{
Record = new Details();
Records = new ObservableCollection<Details>();
}
private void SubmitExecute(object parameter)
{
var TempRecord = new Details();
//TempRecord = Record;
TempRecord.Name = Record.Name;
TempRecord.Company = Record.Company;
TempRecord.Region = Record.Region;
TempRecord.HRA = Record.HRA;
TempRecord.Salary = Record.Salary;
TempRecord.Basic = Record.Basic;
TempRecord.PF = Record.PF;
Records.Add(TempRecord);
}
private bool CanSubmitExecute(object parameter)
{
if (string.IsNullOrEmpty(Record.Name) || string.IsNullOrEmpty(Record.Company))
{
return false;
}
else
{
return true;
}
}
private ICommand _SubmitCommand1;
public ICommand SubmitCommand1
{
get
{
if (_SubmitCommand1 == null)
{
_SubmitCommand1 = new RelayCommand(SubmitExecute1, CanSubmitExecute1, false);
}
return _SubmitCommand1;
}
}
public int findIndex(Details myDetails)
{
int count = 0;
for (int i = 0; i < Records.Count; i++)
{
count = 0;
Details myRecord = Records[i];
if (myRecord.Name == myDetails.Name)
count++;
else continue;
if (myRecord.Company == myDetails.Company)
count++;
else continue;
if (myRecord.Region == myDetails.Region)
count++;
else continue;
if (myRecord.Salary == myDetails.Salary)
count++;
else continue;
if (myRecord.HRA == myDetails.HRA)
count++;
else continue;
if (myRecord.Basic == myDetails.Basic)
count++;
else continue;
if (myRecord.PF == myDetails.PF)
count++;
else continue;
if (count == 7)
return i;
}
return -1;
}
private void SubmitExecute1(object parameter)
{
Records.Remove((Details)parameter);
}
private bool CanSubmitExecute1(object parameter)
{
if (string.IsNullOrEmpty(Record.Name) || string.IsNullOrEmpty(Record.Company))
{
return false;
}
else
{
return true;
}
}
private ICommand _SubmitCommand2;
public ICommand SubmitCommand2
{
get
{
if (_SubmitCommand2 == null)
{
_SubmitCommand2 = new RelayCommand(SubmitExecute2, CanSubmitExecute2, false);
}
return _SubmitCommand2;
}
}
private void SubmitExecute2(object parameter)
{
}
private bool CanSubmitExecute2(object parameter)
{
if (string.IsNullOrEmpty(Record.Name) || string.IsNullOrEmpty(Record.Company))
{
return false;
}
else
{
return true;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string de)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(de));
}
}
}
RelayCommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Assignment_2.Commands
{
class RelayCommand : ICommand
{
Action<object> executeAction;
Func<object, bool> canExecute;
bool canExecuteCache;
public RelayCommand(Action<object> executeAction, Func<object, bool> canExecute, bool canExecuteCache)
{
this.canExecute = canExecute;
this.executeAction = executeAction;
canExecuteCache = canExecuteCache;
}
public bool CanExecute(object parameter)
{
if (canExecute == null)
{
return true;
}
else
{
return canExecute(parameter);
}
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
executeAction(parameter);
}
}
}
MainWindow.xaml
<Window x:Class="Assignment_2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Assignment_2.ViewModel"
Title="MainWindow" Height="401" Width="472">
<Window.Resources>
<vm:DetailsViewModel x:Key="DetailsViewModel"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource DetailsViewModel}}" RenderTransformOrigin="0.5,0.5" Margin="0,0,1,1">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-0.21"/>
<TranslateTransform/>
</TransformGroup>
</Grid.RenderTransform>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="20.59"/>
<RowDefinition Height="22.906"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="29*"/>
<RowDefinition Height="293*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Text=" Name" HorizontalAlignment="Center" Margin="0,0,0,4" Grid.RowSpan="2" Width="35" />
<TextBox Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.Name,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin="2,0,0,1" Grid.RowSpan="2"/>
<TextBlock Text="Company" HorizontalAlignment="Center" Margin="0,2,0,3" Width="51" Grid.RowSpan="2" Grid.Row="1"/>
<TextBox Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.Company,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="2,2,0,1" Grid.Row="1"/>
<TextBlock Grid.Row="2" Text="Region" HorizontalAlignment="Center" Margin="0,1,0,4" Width="37"/>
<TextBox Grid.Row="2" Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.Region,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="3,1,0,4"/>
<TextBlock Grid.Column="1" Text="HRA" HorizontalAlignment="Left" Margin="131,-1,0,2" Width="23"/>
<TextBox Grid.Column="1" Width="83" HorizontalAlignment="Left" Text="{Binding Record.HRA,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="162,0,0,1"/>
<TextBlock Grid.Column="1" Text="Basic" HorizontalAlignment="Left" Margin="129,2,0,13" Width="26" Grid.RowSpan="2" Grid.Row="1"/>
<TextBox Grid.Column="1" Width="81" HorizontalAlignment="Left" Text="{Binding Record.Basic,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="162,2,0,3" Grid.Row="1"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="PF" HorizontalAlignment="Left" Margin="144,1,0,5" Width="12"/>
<TextBox Grid.Row="2" Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.PF,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="162,2,0,1"/>
<TextBlock Grid.Column="1" Text="Salary" HorizontalAlignment="Left" Margin="268,-1,0,1" Width="36"/>
<TextBox Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.Salary,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="304,0,0,21" Grid.RowSpan="2"/>
<Button Content="Add" Command="{Binding SubmitCommand }" HorizontalAlignment="Left" Grid.Row="4" Margin="121,4,0,5" Width="44" Grid.Column="1"/>
<ListView x:Name="ListedView" ItemsSource="{Binding Records}" Width="Auto" Grid.Row="5" Margin="38,3,13,36" Grid.ColumnSpan="2">
<ListView.View >
<GridView >
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="Auto" />
<GridViewColumn Header="Company" DisplayMemberBinding="{Binding Company}" Width="Auto"/>
<GridViewColumn Header="Region" DisplayMemberBinding="{Binding Region}" Width="Auto" />
<GridViewColumn Header="HRA" DisplayMemberBinding="{Binding HRA}" Width="Auto" />
<GridViewColumn Header="Basic" DisplayMemberBinding="{Binding Basic}" Width="Auto" />
<GridViewColumn Header="PF" DisplayMemberBinding="{Binding PF}" Width="Auto" />
<GridViewColumn Header="Salary" DisplayMemberBinding="{Binding Salary}" Width="Auto" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Remove" Margin="1,36,50,150" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListView},Path=DataContext.SubmitCommand1}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Any help is appreciated in this regard. Thanks
If I understand your issue correctly, you should bind the SelectedItem property of the ListView to be able to edit the currently selected item in the TextBox elements above the ListView:
<ListView SelectedItem="{Binding Record}" ... />
With or without Binding you can do it like this :
if ((sender as ListView).SelectedValue != null)
{
string item = (sender as ListView).SelectedValue.ToString();
SelectedIndex = (sender as ListView).SelectedIndex;
list.Items[SelectedIndex] = "Updated";
}
(I like to add a ContextMenu on my ListViewItems so it is easier for users to modify the specified clicked item) :
<ListView x:Name="list">
<ListView.ContextMenu>
<ContextMenu x:Name="contextmenu">
<MenuItem x:Name="Add" Header="Add" Click="Add_Click"/>
<MenuItem x:Name="Del" Header="Delete" Click="Del_Click"/>
<MenuItem x:Name="Update" Header="Update" Click="Update_Click"/>
</ContextMenu>
</ListView.ContextMenu>
</ListView>

WPF Treeview - Define Parent and Children Nodes?

I'm trying to implement a TreeView in WPF to show children that display the charge activity, adjustment activity, etc on a daily account balance. I was going to start by first rendering the daily account balance details as the parent node (which I've done successfully), and then try, for instance, showing a text segment as the child for testing and then expanding from there to show children that I would preferably render as tables to show what was charged that day, and then separately what was adjusted on a charge that day, etc. This is where I'm stuck.
Here's my XAML code:
<Window x:Class="Client_Invoice_Auditor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client_Invoice_Auditor"
xmlns:self="clr-namespace:Client_Invoice_Auditor.CoreClientAR"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="80*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<StackPanel Orientation="Vertical">
<DockPanel VerticalAlignment="Top" Height="20" Panel.ZIndex="1">
<Menu Name="fileMenu" Width="Auto" DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Open Account File" Click="menuOpenFile_Click"/>
<MenuItem Header="Exit" Click="menuExit_Click"/>
</MenuItem>
<MenuItem Header="Options">
<!--<MenuItem Header="Update" Click="update_Click"/>-->
<MenuItem Header="About" Click="about_Click"/>
</MenuItem>
</Menu>
</DockPanel>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Center" Height="Auto">
<StackPanel Width="Auto" Orientation="Horizontal" HorizontalAlignment="Center">
<Border BorderBrush="MediumAquamarine" BorderThickness="2">
<Label Name="AccountNumber"/>
</Border>
<Border BorderBrush="MediumAquamarine" BorderThickness="2">
<Label Name="AcctDesc"/>
</Border>
<Border BorderBrush="MediumAquamarine" BorderThickness="2">
<Label Name="Organization"/>
</Border>
</StackPanel>
</WrapPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Label Margin="20,10,0,0" Content="Activity Date Time" />
<Label Margin="60,10,0,0" Content="Beginning Balance" />
<Label Margin="10,10,0,0" Content="Charge Amount" />
<Label Margin="30,10,0,0" Content="Adjustments" />
<Label Margin="40,10,0,0" Content="Payments" />
<Label Margin="60,10,0,0" Content="End Balance" />
</StackPanel>
</StackPanel>
</Grid>
<Grid Grid.Row="1" Grid.Column="0">
<TreeView Name="DABView">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:dailyAccountBalance}">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Width="150" Text="{Binding DabActivityDate}" />
<TextBlock Width="100" Margin="20,0,0,0" Text="{Binding BegBalance}" />
<TextBlock Width="100" Margin="20,0,0,0" Text="{Binding ChrgAmount}" />
<TextBlock Width="100" Margin="20,0,0,0" Text="{Binding AdjAmount}" />
<TextBlock Width="100" Margin="20,0,0,0" Text="{Binding PmtAmount}" />
<TextBlock Width="100" Margin="20,0,0,0" Text="{Binding EndBalance}" />
</StackPanel>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate x:Key="TestChild">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Text="YOU IZ GOOD" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Grid>
</Window>
My Main Window's codebehind:
using Client_Invoice_Auditor.CoreClientAR;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Client_Invoice_Auditor
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public string accountFile;
public MainWindow()
{
InitializeComponent();
OpenAccountFile();
}
private void OpenAccountFile()
{
accountFile = "";
//errorFilters.Clear();
// Displays an OpenFileDialog so the user can select a file.
Microsoft.Win32.OpenFileDialog openFileDialog1 = new Microsoft.Win32.OpenFileDialog();
openFileDialog1.Filter = "Account Files|*.acct";
openFileDialog1.Title = "Select an account file";
if (openFileDialog1.ShowDialog() == true)
{
// Assign the cursor in the Stream to the Form's Cursor property.
accountFile = openFileDialog1.FileName;
//openFileDialog1.Dispose();
}
if (accountFile == "")
{
/*System.Windows.MessageBox.Show("File must be selected in order to continue - exiting now."
, "No File Selected", MessageBoxButton.OK);
this.Close();*/
if (!AcctDesc.HasContent)
{
AcctDesc.Content = "No account file Loaded";
//Version version = Assembly.GetExecutingAssembly().GetName().Version;
}
}
else
{
//openFileDialog1 = null;
Console.WriteLine("Account file path is: " + accountFile);
DataTable dataAR = new DataTable();
try
{
Tuple<accountARHeader, List<dailyAccountBalance>, DataTable> loadedAR = dabARLoader.LoadARData(accountFile);
//dataAR = loadedAR.Item2;
AccountNumber.Content = "Account Number: " + loadedAR.Item1.AccountNumber;
AcctDesc.Content = "Description: " + loadedAR.Item1.AccountDescription;
Organization.Content = "Client Organization: " + loadedAR.Item1.OrganizationName;
//TreeViewItem dummy = new TreeViewItem();
//dummy.DataContext = "Hi";
//loadedAR.Item2.First().Dummy.Add("La");
//DABView.Items.Add(dummy);
DABView.ItemsSource = loadedAR.Item2;
//DABView.DisplayMemberPath = "A";
}
catch (Exception e)
{
System.Windows.MessageBox.Show("I don't wanna open this file! Try another. Error: " + e.Message);
OpenAccountFile();
}
}
}
private void menuOpenFile_Click(object sender, RoutedEventArgs e)
{
OpenAccountFile();
}
private void menuExit_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void about_Click(object sender, RoutedEventArgs e)
{
System.Windows.MessageBox.Show("I heard you like clicking buttons.");
}
}
}
And finally, my relevant class file:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace Client_Invoice_Auditor.CoreClientAR
{
public class dailyAccountBalance
{
double dabID;
DateTime dabActivityDate;
double begBalance;
double endBalance;
double chrgAmount;
double adjAmount;
double pmtAmount;
string dabActivitySegment;
public double DabID { get => dabID; set => dabID = value; }
public double BegBalance { get => begBalance; set => begBalance = value; }
public double EndBalance { get => endBalance; set => endBalance = value; }
public string DabActivitySegment { get => dabActivitySegment; set => dabActivitySegment = value; }
public DateTime DabActivityDate { get => dabActivityDate; set => dabActivityDate = value; }
public double ChrgAmount { get => chrgAmount; set => chrgAmount = value; }
public double AdjAmount { get => adjAmount; set => adjAmount = value; }
public double PmtAmount { get => pmtAmount; set => pmtAmount = value; }
public ObservableCollection<dailyAccountBalance> Items { get; set; }
public List<string> Dummy { get => dummy; set => dummy = value; }
public IList<TreeViewItem> DummyItems { get => dummyItems; set => dummyItems = value; }
public dailyAccountBalance()
{
this.Items = new ObservableCollection<dailyAccountBalance>();
}
List<string> dummy = new List<string>();
IList<TreeViewItem> dummyItems;
}
}
Is there a way, for instance, that I can show a dummy string as ONE child in the TreeView when it expands? Do I need to implement expand methods for this?
Thanks so much in advance.
Typically what I've done in the past is add a DummyNode to the TreeNode, bind the "IsExpanded" TreeViewItem property in the TreeViewItem style to a IsExpanded property on my Node class in my View Model, and add a bool _isLoaded flag. Then when the IsExpanded it triggered I check if _isLoaded is true, and if not I then go and load all of the subsequent data and set the _isLoaded to true.
btw... I use Prism to give me my BindableBase class that implements the INotifyPropertyChanged event for OnPropertyChanged. You can choose to implement that yourself instead of using BindableBase.
Example:
Node Class
public class TreeData : BindableBase
{
private bool _isExpanded = false;
private bool _isSelected = false;
private bool _hasChildren = false;
private Func<List<TreeData>> _getChildrenMethod;
private bool _isLoaded = false;
public TreeData(string value, bool hasChildren, Func<List<TreeData>> getChildren)
{
Value = value;
_hasChildren = hasChildren;
_getChildrenMethod = getChildren;
if(hasChildren)
{
Children.Add(new TreeNode("Dummy", false, null));
}
}
public ObservableCollection<TreeData> Children { get; set; } = new ObservableCollection<TreeData>();
public string Value {get;set;}
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; OnPropertyChanged(); }
}
public bool IsExpanded
{
get { return _isExpanded; }
set
{
_isExpanded = value;
if(!_isLoaded)
{
// Call load method here
if(_addChildrenMethod != null && _hasChildren)
{
Children.Clear();
Children.AddMany(_addChildrenMethod());
}
_isLoaded = true;
}
OnPropertyChanged();
}
}
}
View Model Class (owns the list of items)
public class BusinessViewModel
{
public ObservableCollection<TreeData> Items { get; set; } = new ObservableCollection<TreeData>();
public BusinessViewModel()
{
Items.Add(new TreeData("Root Item", hasChildren: true, GetChildren));
}
public List<TreeData> GetChildren()
{
return new List<TreeData>()
{
new TreeData("Child", hasChildren: false, null);
};
}
}
Then the XAML
<TreeView ItemSource="{Binding Items}" >
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.Resources>
</TreeView>
Hope that helps :D

How to get original value in TreeView?

I try get original value on WPF treeview.
Commonly, Treeview we get selected item' original value using
object Item = treeview1.SelectedItem;
MessageBox.Show(Item.ToString());
but, my attempts to get it using this method were unsuccessful.
If I try it, then I get a "WPFName+TreeItem" MessageBox
This is my code with WPF
C#
private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
object temp = treeView.SelectedItem;
MessageBox.Show(temp.ToString());
}
private static IEnumerable<TreeItem> GetChannelTreeForTreeView(QueryRunner queryRunner)
{
List<ChannelTreeItem> channelTree = queryRunner.Utils.GetChannelTree(false);
foreach (ChannelTreeItem channelTreeItem in channelTree)
{
TreeItem treeViewItem = new TreeItem { Data = channelTreeItem.Channel };
FillTreeViewItem(treeViewItem, channelTreeItem);
yield return treeViewItem;
}
}
private static void FillTreeViewItem(TreeItem treeViewItem, ChannelTreeItem channelTreeItem)
{
foreach (ClientListEntry clientListEntry in channelTreeItem.Clients)
if (clientListEntry.Nickname.Contains("serveradmin from") == false)
{
treeViewItem.Children.Add(new TreeItem { Data = clientListEntry });
}
foreach (ChannelTreeItem childChannelTreeItem in channelTreeItem.Children)
{
TreeItem childTreeViewItem = new TreeItem { Data = childChannelTreeItem.Channel };
treeViewItem.Children.Add(childTreeViewItem);
FillTreeViewItem(childTreeViewItem, childChannelTreeItem);
}
}
public class TreeItem
{
public object Data { get; set; }
public List<TreeItem> Children { get; private set; }
public TreeItem()
{
Children = new List<TreeItem>();
}
}
WPF
<TreeView x:Name="treeView" HorizontalAlignment="Left"
Height="265" VerticalAlignment="Top" Width="353"
SelectedItemChanged="treeView_SelectedItemChanged"
MouseUp="treeView_MouseUp">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:ViewItemWPF+TreeItem}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding Data}"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type HelperClasses:ChannelListEntry}">
<StackPanel Orientation="Horizontal">
<Border Background="Green" Width="8" Height="12" BorderBrush="#00000000"></Border>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type HelperClasses:ClientListEntry}" >
<StackPanel Orientation="Horizontal">
<Border Background="DarkBlue" Width="8" Height="12" BorderBrush="#00000000"></Border>
<TextBlock Text="{Binding Path=Nickname}" Foreground="blue" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Can I get original value using foreach or others methods?
You could cast the SelectedItem property to a TreeItem and then cast its Data property to the appropriate type. You can then access any properties of your classes as usual.
It is unclear what type "Channel" is but the following sample code should give you the idea:
private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
TreeItem temp = treeView.SelectedItem as TreeItem;
if (temp != null)
{
ClientListEntry clientListEntry = temp.Data as ClientListEntry;
if (clientListEntry != null)
{
//Data is a ClientListEntry
//...
return;
}
Channel channel = temp.Data as ClientListEntry;
if (channel != null)
{
//...
}
}
MessageBox.Show(temp.ToString());
}

Not getting value from StackPanel

I have been having a problem with getting the exact value from the stackpanel.
XAML
<Page
x:Class="MedicinesApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MedicinesApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="MedicinesCollectionViewSource" IsSourceGrouped="True" />
</Page.Resources>
<GridView
ItemsSource="{Binding Source={StaticResource MedicinesCollectionViewSource}}"
x:Name="MedicinesGridView"
Padding="30,20,40,0"
SelectionMode="Single"
IsSwipeEnabled="false"
IsItemClickEnabled="True" SelectionChanged="MedicinesGridView_SelectionChanged"
ItemClick="MedicinesGridView_ItemClick">
<GridView.ItemTemplate>
<DataTemplate x:Name="templateTrending">
<Grid HorizontalAlignment="Left" Width="250" Height="250" x:Name="wow">
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding Path=DiseaseImageSource}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
<StackPanel Orientation="Vertical">
<TextBlock Text="Disease Name" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextBlockStyle}" Height="30" Margin="15,0,15,0"/>
<TextBlock x:Name="DiseaseNameBro" Text="{Binding Path=DiseaseName, Mode=TwoWay}" Style="{StaticResource TitleTextBlockStyle}" Height="30" Margin="15,0,15,0"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock Text="Category of Disease" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,87,10"/>
<TextBlock Text="{Binding Path=CategoryOfDisease}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text='{Binding Key}' Foreground="Gray" Margin="5" FontSize="30" FontFamily="Segoe UI Light" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid MaximumRowsOrColumns="2" Orientation="Horizontal" />
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid GroupPadding="0,0,70,0" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
C#
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.UI.Xaml.Controls;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace MedicinesApp
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public string getMedicinesName;
public ObservableCollection<Disease> diseaseList = new ObservableCollection<Disease>();
public MainPage()
{
this.InitializeComponent();
diseaseList.Add(new Disease { DiseaseName = "Malaria", DiseaseImageSource = "/Assets/133px-Malaria.jpg", CategoryOfDisease = "Blood" });
diseaseList.Add(new Disease { DiseaseName = "Typhod", DiseaseImageSource = "../Assets/apple.jpg", CategoryOfDisease = "Thorat" });
diseaseList.Add(new Disease { DiseaseName = "Cancer", DiseaseImageSource = "../Assets/orange.jpg", CategoryOfDisease = "Body" });
diseaseList.Add(new Disease { DiseaseName = "Headache", DiseaseImageSource = "../Assets/bilberry.jpg", CategoryOfDisease = "Brain" });
//diseaseList = new ObservableCollection<Disease>(diseaseList.OrderBy(c => c.DiseaseName));
MedicinesCollectionViewSource.Source = GetGroupsByLetter();
}
internal List<GroupInfoList<object>> GetGroupsByLetter()
{
var groups = new List<GroupInfoList<object>>();
var query = from item in diseaseList
orderby item.DiseaseName
group item by item.DiseaseName[0] into g
select new { GroupName = g.Key, Items = g };
foreach (var g in query)
{
var info = new GroupInfoList<object>();
info.Key = g.GroupName;
foreach (var item in g.Items)
{
info.Add(item);
}
groups.Add(info);
}
return groups;
}
public class GroupInfoList<T> : List<object>
{
public object Key { get; set; }
public new IEnumerator<object> GetEnumerator()
{
return base.GetEnumerator();
}
}
public class Disease
{
public string DiseaseName { get; set; }
public string DiseaseImageSource { get; set; }
public string CategoryOfDisease { get; set; }
}
private void MedicinesGridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
getMedicinesName = ((Disease)MedicinesGridView.SelectedItem).DiseaseName;
}
private void MedicinesGridView_ItemClick(object sender, ItemClickEventArgs e)
{
this.Frame.Navigate(typeof(malaria), getMedicinesName);
}
}
the problem is whenever I click on Malaria,
it goes to the other page and shows cancer, which is the first vaule in the list.
How can I get the value of the item I click and not of the first item. I Have attached screenshot for you'll to get a better knowledge of things.
I looked at your code and there is one thing that I don't understand.
Why are you using 2 events to navigate. Only one would do the job.
private void MedicinesGridView_ItemClick(object sender, ItemClickEventArgs e)
{
var selectedDisease = e.ClickedItem as Disease;
this.Frame.Navigate(typeof(malaria), selectedDisease);
}
You don't need the getMedicinesName either.
Don't forget to remove the SelectionChanged event both from you code behind and XAML.

WPF Listbox layout: multiple columns

I have a ListBox (WPF) that contains CheckBoxes. I'm using is in a configuration screen. Schematic example below:
Now I want to add a "Test 5" CheckBox. I have limited vertical space, so I want it to appear in the horizontal direction, as shown below:
Can the ListBox layout be modified so the CheckBoxes will be arranged like this?
<ListBox Name="CategoryListBox"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Path=RefValues,
UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Multiple">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal"
MinWidth="150" MaxWidth="150"
Margin="0,5, 0, 5" >
<CheckBox
Name="checkedListBoxItem"
IsChecked="{Binding
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem} },
Path=IsSelected, Mode=TwoWay}" />
<ContentPresenter
Content="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=Content}"
Margin="5,0, 0, 0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
or as simple as this:
<Grid>
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem>listbox item 1</ListBoxItem>
<ListBoxItem>listbox item 2</ListBoxItem>
<ListBoxItem>listbox item 3</ListBoxItem>
<ListBoxItem>listbox item 4</ListBoxItem>
<ListBoxItem>listbox item 5</ListBoxItem>
</ListBox>
</Grid>
I encountered a similar problem and eibhrum's answer gave me some idea. I used the following code and I think this also what you need. I used UniformGrid instead of WrapPanel.
<ListBox HorizontalAlignment="Stretch"
ItemsSource="{Binding Timers}"
>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<!-- UNIFORM GRID HERE -->
<UniformGrid Columns="3" IsItemsHost="True"
HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border>
<StackPanel Orientation="Vertical" >
<TextBlock Text="{Binding Label}" TextWrapping="Wrap"/>
<Separator Margin="5,0,10,0"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I know this is an older post, but I stumbled across a fairly simple way to do this while I was trying to solve the same problem here: http://social.technet.microsoft.com/wiki/contents/articles/19395.multiple-columns-in-wpf-listbox.aspx
Just add your binding data source (or add items as needed).
<ListBox Name="myLB" ScrollViewer.HorizontalScrollBarVisiblity="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
ListBox with multi column and ListBoxItem Orientation is Vertical.
ListBox has fix Height and Auto Width. When add ListBoxItem, ListBox will Auto increase Width.
<ListBox Height="81" VerticalAlignment="Top" HorizontalAlignment="Left" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem Content="1"/>
<ListBoxItem Content="2"/>
<ListBoxItem Content="3"/>
<ListBoxItem Content="4"/>
<ListBoxItem Content="5"/>
<ListBoxItem Content="6"/>
<ListBoxItem Content="7"/>
<ListBoxItem Content="8"/>
<ListBoxItem Content="9"/>
<ListBoxItem Content="10"/>
</ListBox>
ListBox with multi column and ListBoxItem Orientation is Horizontal. ListBox has fix Width and Auto Height. When add ListBoxItem, ListBox will Auto increase Height.
<ListBox Width="81" VerticalAlignment="Top" HorizontalAlignment="Left" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem Content="1"/>
<ListBoxItem Content="2"/>
<ListBoxItem Content="3"/>
<ListBoxItem Content="4"/>
<ListBoxItem Content="5"/>
<ListBoxItem Content="6"/>
<ListBoxItem Content="7"/>
<ListBoxItem Content="8"/>
<ListBoxItem Content="9"/>
<ListBoxItem Content="10"/>
</ListBox>
If you need to flow rows between multiple regions (multiple windows, in my case), you can use a custom panel implementation.
Example usage:
<Grid>
<Grid.Resources>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate">
<local:SharedLayoutStackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
<local:SharedLayoutCoordinator x:Key="slc" ItemsSource="{Binding Path=MyItems}" />
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Margin="10,10,5,10" BorderBrush="Black" BorderThickness="1">
<ListBox
local:SharedLayoutCoordinator.Region="{Binding Source={StaticResource slc}, Path=[0]}"
ItemsPanel="{StaticResource ResourceKey=ItemsPanelTemplate}" />
</Border>
<Border Grid.Column="1" Margin="5,10" BorderBrush="Black" BorderThickness="1">
<ListBox
local:SharedLayoutCoordinator.Region="{Binding Source={StaticResource slc}, Path=[1]}"
ItemsPanel="{StaticResource ResourceKey=ItemsPanelTemplate}" />
</Border>
<Border Grid.Column="2" Margin="5,10,10,10" BorderBrush="Black" BorderThickness="1">
<ListBox
local:SharedLayoutCoordinator.Region="{Binding Source={StaticResource slc}, Path=[2]}"
ItemsPanel="{StaticResource ResourceKey=ItemsPanelTemplate}" />
</Border>
</Grid>
Results in:
Full demo implementation is available on Github, but the key bits are below.
SharedLayoutCoordinator.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace MultiRegionListBox
{
internal class SharedLayoutCoordinator : DependencyObject
{
private List<SharedLayoutRegion> Regions = new List<SharedLayoutRegion>();
public SharedLayoutRegion this[int index]
{
get
{
var slr = new SharedLayoutRegion(this, index);
for (int i = 0; i < Regions.Count; i++)
{
if (Regions[i].Index > index)
{
Regions.Insert(i, slr);
return slr;
}
}
Regions.Add(slr);
return slr;
}
}
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(SharedLayoutCoordinator), new PropertyMetadata(null));
public static SharedLayoutRegion GetRegion(DependencyObject obj)
{
return (SharedLayoutRegion)obj.GetValue(RegionProperty);
}
public static void SetRegion(DependencyObject obj, SharedLayoutRegion value)
{
obj.SetValue(RegionProperty, value);
}
public static readonly DependencyProperty RegionProperty =
DependencyProperty.RegisterAttached("Region", typeof(SharedLayoutRegion),
typeof(SharedLayoutCoordinator), new PropertyMetadata(null, Region_Changed));
private static void Region_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var itemsControl = (ItemsControl)d;
var newController = (SharedLayoutRegion)e.NewValue;
if (newController == null)
{
return;
}
itemsControl.SetBinding(ItemsControl.ItemsSourceProperty, new Binding(nameof(ItemsSource)) { Source = newController.Coordinator });
}
public static SharedLayoutRegion GetParentSharedLayoutController(DependencyObject obj)
{
while (obj != null)
{
if (obj is ItemsControl ic)
{
var slc = GetRegion(ic);
if (slc != null)
{
return slc;
}
}
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
public IEnumerable<SharedLayoutRegion> GetPreceedingRegions(SharedLayoutRegion region)
{
return Regions.Where(r => r.Index < region.Index);
}
internal SharedLayoutRegion GetNextRegion(SharedLayoutRegion region)
{
var idx = Regions.IndexOf(region);
if (idx + 1 < Regions.Count)
{
return Regions[idx + 1];
}
return null;
}
internal SharedLayoutRegion GetPreviousRegion(SharedLayoutRegion region)
{
var idx = Regions.IndexOf(region);
if (idx > 0)
{
return Regions[idx - 1];
}
return null;
}
}
internal class SharedLayoutRegion
{
private Action InvalidateMeasureCallback;
public SharedLayoutRegion(SharedLayoutCoordinator coord, int index)
{
this.Coordinator = coord;
this.Index = index;
}
public SharedLayoutCoordinator Coordinator { get; }
public int Index { get; }
public SharedLayoutStackPanel Panel { get; set; }
public bool IsMeasureValid
=> !(Panel == null || !Panel.IsMeasureValid || Panel.IsMeasureMeaningless);
internal bool CanMeasure(Action invalidateMeasure)
{
if (Coordinator.GetPreceedingRegions(this).All(pr => pr.IsMeasureValid))
{
return true;
}
this.InvalidateMeasureCallback = invalidateMeasure;
return false;
}
public int StartOfRegion => Coordinator.GetPreviousRegion(this)?.EndOfRegion ?? 0;
public int CountInRegion { get; set; }
public int EndOfRegion => CountInRegion + StartOfRegion;
public bool HasNextRegion => Coordinator.GetNextRegion(this) != null;
internal void OnMeasure()
{
var nextRegion = Coordinator.GetNextRegion(this);
if (nextRegion != null && nextRegion.InvalidateMeasureCallback != null)
{
nextRegion.InvalidateMeasureCallback();
nextRegion.InvalidateMeasureCallback = null;
}
}
}
}
SharedLayoutStackPanel.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace MultiRegionListBox
{
class SharedLayoutStackPanel : Panel, IScrollInfo
{
internal const double _scrollLineDelta = 16.0;
public void LineUp() => SetVerticalOffset(VerticalOffset - _scrollLineDelta);
public void LineDown() => SetVerticalOffset(VerticalOffset + _scrollLineDelta);
public void LineLeft() => SetHorizontalOffset(HorizontalOffset - 1.0);
public void LineRight() => SetHorizontalOffset(HorizontalOffset + 1.0);
public void PageUp() => SetVerticalOffset(VerticalOffset - ViewportHeight);
public void PageDown() => SetVerticalOffset(VerticalOffset + ViewportHeight);
public void PageLeft() => SetHorizontalOffset(HorizontalOffset - ViewportWidth);
public void PageRight() => SetHorizontalOffset(HorizontalOffset + ViewportWidth);
public void MouseWheelUp() => SetVerticalOffset(VerticalOffset - SystemParameters.WheelScrollLines * _scrollLineDelta);
public void MouseWheelDown() => SetVerticalOffset(VerticalOffset + SystemParameters.WheelScrollLines * _scrollLineDelta);
public void MouseWheelLeft() => SetHorizontalOffset(HorizontalOffset - 3.0 * _scrollLineDelta);
public void MouseWheelRight() => SetHorizontalOffset(HorizontalOffset + 3.0 * _scrollLineDelta);
public double ExtentWidth => Extent.Width;
public double ExtentHeight => Extent.Height;
public double ViewportWidth => Viewport.Width;
public double ViewportHeight => Viewport.Height;
public double HorizontalOffset => ComputedOffset.X;
public double VerticalOffset => ComputedOffset.Y;
public void SetHorizontalOffset(double offset)
{
if (double.IsNaN(offset))
{
throw new ArgumentOutOfRangeException();
}
if (offset < 0d)
{
offset = 0d;
}
if (offset != Offset.X)
{
Offset.X = offset;
InvalidateMeasure();
}
}
/// <summary>
/// Set the VerticalOffset to the passed value.
/// </summary>
public void SetVerticalOffset(double offset)
{
if (double.IsNaN(offset))
{
throw new ArgumentOutOfRangeException();
}
if (offset < 0d)
{
offset = 0d;
}
if (offset != Offset.Y)
{
Offset.Y = offset;
InvalidateMeasure();
}
}
public ScrollViewer ScrollOwner
{
get { return _scrollOwner; }
set
{
if (value == _scrollOwner)
{
return;
}
InvalidateMeasure();
Offset = new Vector();
Viewport = Extent = new Size();
_scrollOwner = value;
}
}
public bool CanVerticallyScroll
{
get { return true; }
set { /* noop */ }
}
public bool CanHorizontallyScroll
{
get { return false; }
set { /* noop */ }
}
internal bool IsMeasureMeaningless { get; private set; }
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
this.SLC = SharedLayoutCoordinator.GetParentSharedLayoutController(this);
if (SLC != null)
{
this.SLC.Panel = this;
}
InvalidateMeasure();
}
protected override Size MeasureOverride(Size viewportSize)
{
if (SLC == null || !SLC.CanMeasure(InvalidateMeasure))
{
IsMeasureMeaningless = true;
return viewportSize;
}
IsMeasureMeaningless = false;
var extent = new Size();
var countInRegion = 0; var hasNextRegion = SLC.HasNextRegion;
foreach (var child in InternalChildren.Cast<UIElement>().Skip(SLC.StartOfRegion))
{
child.Measure(new Size(viewportSize.Width, double.PositiveInfinity));
var childDesiredSize = child.DesiredSize;
if (hasNextRegion && extent.Height + childDesiredSize.Height > viewportSize.Height)
{
break;
}
extent.Width = Math.Max(extent.Width, childDesiredSize.Width);
extent.Height += childDesiredSize.Height;
SLC.CountInRegion = countInRegion += 1;
}
// Update ISI
this.Extent = extent;
this.Viewport = viewportSize;
this.ComputedOffset.Y = Bound(Offset.Y, 0, extent.Height - viewportSize.Height);
this.OnScrollChange();
SLC.OnMeasure();
return new Size(
Math.Min(extent.Width, viewportSize.Width),
Math.Min(extent.Height, viewportSize.Height));
}
private static double Bound(double c, double min, double max)
=> Math.Min(Math.Max(c, Math.Min(min, max)), Math.Max(min, max));
protected override Size ArrangeOverride(Size arrangeSize)
{
if (IsMeasureMeaningless)
{
return arrangeSize;
}
double cy = -ComputedOffset.Y;
int i = 0, i_start = SLC.StartOfRegion, i_end = SLC.EndOfRegion;
foreach (UIElement child in InternalChildren)
{
if (i >= i_start && i < i_end)
{
child.Arrange(new Rect(0, cy, Math.Max(child.DesiredSize.Width, arrangeSize.Width), child.DesiredSize.Height));
cy += child.DesiredSize.Height;
}
else if (child.RenderSize != new Size())
{
child.Arrange(new Rect());
}
i += 1;
}
return arrangeSize;
}
private void OnScrollChange() => ScrollOwner?.InvalidateScrollInfo();
public Rect MakeVisible(Visual visual, Rect rectangle)
{
// no-op
return rectangle;
}
internal ScrollViewer _scrollOwner;
internal Vector Offset;
private Size Viewport;
private Size Extent;
private Vector ComputedOffset;
private SharedLayoutRegion SLC;
}
}
My Solution : Using a WrapPanel with a vertical Orientation. Work fine if you adjust the height of the WrapPanel to the Height of the ListBox.
<ListBox ItemsSource="{Binding mySource}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem IsChecked="{Binding checked}">
<CheckBox IsChecked="{Binding checked}" Content="{Binding Name}" />
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Categories