Editable Grid In Windows 10 UWP App - c#

I am using the below listview to create the product interface, how can i make the selected item editable in this view. I want to put each cell of selected row in TextBox instead of TextBlock, so that user can edit the values. Looking for help from community.
<Grid>
<StackPanel>
<ListView Name="ItemsList" ItemsSource="{x:Bind products}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Product">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="5,0" />
</Style>
</Grid.Resources>
<TextBlock Grid.Column="0" Text="{x:Bind ProductId}" />
<TextBlock Grid.Column="1" Text="{x:Bind ProductName}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>

#Jackie's suggestion to use a TextBox all the time, making it read-only when the item is not selected, is a good one; it simplifies logic and layout. (Switching between a visible TextBox and a visible TextBlock is tricky, because you want the text to be in exactly the same place.)
For illustration purposes I'll use this simplified model object:
public sealed class Item : INotifyPropertyChanged
{
private bool isReadOnly = true;
public bool IsReadOnly
{
get
{
return isReadOnly;
}
set
{
isReadOnly = value;
OnPropertyChanged();
}
}
public string Value { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Next, the ListView:
<ListView SelectionChanged="OnSelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<TextBox
Text="{Binding Value}"
IsReadOnly="{Binding IsReadOnly}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Items>
<local:Item Value="One" />
<local:Item Value="Two" />
<local:Item Value="Three" />
<local:Item Value="Four" />
</ListView.Items>
</ListView>
Finally, the code-behind, which toggles the IsReadOnly property of the Item:
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
Item removed = e.RemovedItems.FirstOrDefault() as Item;
if (removed != null)
{
removed.IsReadOnly = true;
}
Item added = e.AddedItems.FirstOrDefault() as Item;
if (added != null)
{
added.IsReadOnly = false;
}
}

Related

Expander expands only after file is loaded

Image :
<Expander Grid.Row="1" IsExpanded="False" Background="Yellow">
<Grid Background="Yellow">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Filters" />
<CheckBox Grid.Column="1" Content="A"/>
<CheckBox Grid.Column="2" Content="B"/>
<CheckBox Grid.Column="3" Content="C"/>
</Grid>
</Expander>
I want my expander to expand only after a file is loaded. Please help me how to do that. I am using command to the load button.
YourView.xaml
<Window x:Class="WpfSample.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:converter="clr-namespace:WpfSample.Converters"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<converter:IntToBoolConverter x:Key="intToBoolConverter"/>
</Window.Resources>
<Grid>
<Expander Header="Main Expander" IsEnabled="{Binding Items, Converter={StaticResource intToBoolConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsExpanded="{Binding Items, Converter={StaticResource intToBoolConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Background="Yellow">
<ItemsControl x:Name="expanderItem" ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Sub-Heading">
<ListBox ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch"/>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</Grid>
</Window>
I have created Items as ObservableCollection<string>. You can changed it to your Model.
YourViewModel.cs
private ObservableCollection<string> items;
public ObservableCollection<string> Items
{
get { return items; }
set { items = value; OnPropertyChanged(); }
}
public void LoadExpanderData(object obj)
{
Items = new ObservableCollection<string>();
Items.Add("Item 1");
Items.Add("Item 2");
Items.Add("Item 3");
Items.Add("Item 4");
Items.Add("Item 5");
}
IntToBoolConverter.cs
public class IntToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isExpand = false;
if (value != null && value.GetType().GetProperties().Length > 0)
return isExpand = true;
return isExpand;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return true;
}
}
here is the solution binding a Flag to the property IsEnabled:
for example you set Flag = True when your file is loaded
MainWindow.xaml.cs:
public partial class MainWindow : INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void ButtonClick(object sender, RoutedEventArgs e)
{
Flag = true;
OnPropertyChanged("Flag");
}
public bool Flag { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string property)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
MainWindow.xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Expander Grid.Row="0" IsExpanded="False" Background="Yellow" Margin="50,50,200,50" >
<Expander.Style>
<Style>
<Setter Property="Expander.IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Flag}" Value="True">
<Setter Property="Expander.IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<Grid Background="Yellow">
:
:
</Grid>
</Expander>
<Button Grid.Row="1" Click="ButtonClick" Content="Click Me" />
</Grid>
i have played with IsEnabled but you can bind with the property IsExpanded:
<Expander Grid.Row="0" Background="Yellow" Margin="50,50,200,50" >
<Expander.Style>
<Style>
<Setter Property="Expander.IsExpanded" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Flag}" Value="True">
<Setter Property="Expander.IsExpanded" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
even both if you want:
<Expander Grid.Row="0" Background="Yellow" Margin="50,50,200,50" >
<Expander.Style>
<Style>
<Setter Property="Expander.IsEnabled" Value="False" />
<Setter Property="Expander.IsExpanded" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Flag}" Value="True">
<Setter Property="Expander.IsEnabled" Value="True" />
<Setter Property="Expander.IsExpanded" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
here, when you click on button, the Expander becomes Expanded/Enabled

Autosize column in ListView (UWP)

I would like to arrange my items in a ListView with a Text and an image like in this example:
The first column is automatic, so it expands to the widest element.
The second column should expand to fill the remaining space.
So, how can I make an Auto column... or simulate it?
Obviously, the difficult part of this layout is that different elements have their widths depending on other element's widths.
EDIT: Using #WPInfo's answer, I've tried with this:
XAML
<Page.DataContext>
<local:MainViewModel></local:MainViewModel>
</Page.DataContext>
<ListView ItemsSource="{Binding Items}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding}" />
<Border Background="CornflowerBlue" Grid.Column="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CODE:
public class MainViewModel
{
public IList<string> Items { get; }
public MainViewModel()
{
Items = new List<string>()
{
"ABC",
"ABCDEF",
"ABCDEFGHI",
};
}
}
The expected result was:
However, the actual result is:
The goal is that the first column to be as wide as the widest element inside it.
In WPF, this is solved easily with the use of Grid.SharedSizeGroup. In UWP there is not such a thing.
The problem is that ListView children don't know anything about each other. There's no simple trick to have all the items know what the longest text item is, so a more complicated solution is required. The following works. It will determine a fixed TextBlock size based on the largest item from the string list, and it will update it should the font size change. Obviously a simpler solution closer resembling your example code can be had if you decide on a fixed column size for either the text or the image, but I assume you already know that.
XAML:
<Page.DataContext>
<local:MainViewModel></local:MainViewModel>
</Page.DataContext>
<ListView ItemsSource="{Binding Items}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding MyText, Mode=OneWay}" FontSize="{Binding ItemFontSize, Mode=OneWay}" Width="{Binding TextWidth, Mode=OneWay}" />
<Border Background="CornflowerBlue" Grid.Column="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C# ViewModel
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<MyListViewItemsClass> Items { get; set; }
private List<string> ItemsFromModel;
private int _myFontSize;
public int MyFontSize
{
get { return _myFontSize; }
set
{
if (value != _myFontSize)
{
_myFontSize = value;
OnPropertyChanged("MyFontSize");
// Font size changed, need to refresh ListView items
LoadRefreshMyListViewItems();
}
}
}
public MainViewModel()
{
// set default font size
_myFontSize = 20;
// example data
ItemsFromModel = new List<string>()
{
"ABC",
"ABCDEF",
"ABCDEFGHI",
};
LoadRefreshMyListViewItems();
}
public void LoadRefreshMyListViewItems()
{
int itemMaxTextLength = 0;
foreach (var modelItem in ItemsFromModel)
{
if (modelItem.Length > itemMaxTextLength) { itemMaxTextLength = modelItem.Length; }
}
Items = new ObservableCollection<MyListViewItemsClass>();
// Convert points to pixels, multiply by max character length to determine fixed textblock width
// This assumes 96 DPI. Search for how to grab system DPI in C# there are answers on SO.
double width = MyFontSize * 0.75 * itemMaxTextLength;
foreach (var itemFromModel in ItemsFromModel)
{
var item = new MyListViewItemsClass();
item.MyText = itemFromModel;
item.ItemFontSize = MyFontSize;
item.TextWidth = width;
Items.Add(item);
}
OnPropertyChanged("Items");
}
public class MyListViewItemsClass
{
public string MyText { get; set; }
public int ItemFontSize { get; set; }
public double TextWidth { get; set; }
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
public void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Try setting ItemContainerStyle property for the ListView:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
Then your ListView may look like this:
<ListView x:Name="list">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock ... />
<Image Grid.Column="1" HorizontalAlignment="Right" ... />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
After that, you can set a proper value for HorizontalAlignment property of your ListView.

Binding WPF Control Name to different Control

Have style for a series of buttons btn1, btn2, btn3, etc.
Inside the style for each button is a TextBlock for displaying the "Content" of the button, (since the border inside the style covers any content of the button itself).
Now, I would like for the TextBlock name to be tied to the button name. For example - btn1's text block's name would be btn1Txt. The purpose of this would be the end user can assign each button its own text in a settings menu.
Any hints on how I would go about this? I admit I'm relatively new to WPF and bindings.
EDIT:::: WHAT I"VE GOT SO FAR THAT IS WORKING.
On load, the program checks the settings file for the Text for each button. Each button's content is assigned the proper information. Then inside the style, I bind the TextBlock Text to the content of the parent button.
This may not be the normal way of going about it, but it works
Method
List<string> MainButtons = Properties.Settings.Default.MainButtonNames.Cast<string>().ToList();
for (int i = 0; i < MainButtons.Count(); i++)
{
string actualNum = Convert.ToString((i + 1));
var MainButtonFinder = (Button)this.FindName("MainButton" + actualNum);
Console.WriteLine(MainButtonFinder.Name);
MainButtonFinder.Content = MainButtons[i];
Console.WriteLine(MainButtonFinder.Content);
}
Style
<Style x:Key="MainButtonStyle" TargetType="{x:Type Button}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Width" Value="100px"/>
<Setter Property="MinHeight" Value="50"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="20" Height="45" Width="100" Margin="0" Background="#FF99CCFF">
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Content}" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="LCARS" Foreground="White" Padding="5px" FontSize="18px" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>`
This is the wrong way to go about what you're trying to do. Here's the "right" way to do it. There's a fair amount of boilerplate code here, but you get used to it.
Write a button viewmodel and give your main viewmodel an ObservableCollection of those:
#region ViewModelBase Class
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
#endregion INotifyPropertyChanged
}
#endregion ViewModelBase Class
#region MainViewModel Class
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
ButtonItems.Add(new ButtonItemViewModel("First Command", "First Item", () => MessageBox.Show("First Item Executed")));
ButtonItems.Add(new ButtonItemViewModel("Second Command", "Second Item", () => MessageBox.Show("Second Item Executed")));
}
#region ButtonItems Property
public ObservableCollection<ButtonItemViewModel> ButtonItems { get; }
= new ObservableCollection<ButtonItemViewModel>();
#endregion ButtonItems Property
}
#endregion MainViewModel Class
#region ButtonItemViewModel Class
public class ButtonItemViewModel : ViewModelBase
{
public ButtonItemViewModel(String cmdName, String text, Action cmdAction)
{
CommandName = cmdName;
Text = text;
Command = new DelegateCommand(cmdAction);
}
#region Text Property
private String _text = default(String);
public String Text
{
get { return _text; }
set
{
if (value != _text)
{
_text = value;
OnPropertyChanged();
}
}
}
#endregion Text Property
#region CommandName Property
private String _commandName = default(String);
public String CommandName
{
get { return _commandName; }
private set
{
if (value != _commandName)
{
_commandName = value;
OnPropertyChanged();
}
}
}
#endregion CommandName Property
public ICommand Command { get; private set; }
}
#endregion ButtonItemViewModel Class
public class DelegateCommand : ICommand
{
public DelegateCommand(Action action)
{
_action = action;
}
private Action _action;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_action?.Invoke();
}
}
Make that MainViewModel the DataContext of your Window:
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
And here's how you can put it all together in the XAML:
<Grid>
<StackPanel Orientation="Vertical">
<GroupBox Header="Buttons">
<ItemsControl ItemsSource="{Binding ButtonItems}" HorizontalAlignment="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Margin="2"
MinWidth="80"
Content="{Binding Text}"
Command="{Binding Command}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
<GroupBox Header="Edit Buttons">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox
Grid.Column="0"
Margin="2"
x:Name="ButtonEditorListBox"
ItemsSource="{Binding ButtonItems}"
HorizontalAlignment="Left"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2"
Text="{Binding CommandName}"
/>
<TextBlock
Margin="2"
Text="{Binding Text, StringFormat=': "{0}"'}"
/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
<ContentControl
Grid.Column="1"
Margin="8,2,2,2"
Content="{Binding SelectedItem, ElementName=ButtonEditorListBox}"
>
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock
Margin="2"
HorizontalAlignment="Stretch"
FontWeight="Bold"
Text="{Binding CommandName, StringFormat={}{0}: }"
/>
<TextBox
Margin="2"
HorizontalAlignment="Stretch"
Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
/>
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</GroupBox>
</StackPanel>
</Grid>
Back to your question:
Inside the style for each button is a TextBlock for displaying the "Content" of the button, (since the border inside the style covers any content of the button itself).
You're doing styles wrong. Very, very wrong. I can help you fix it if you show me the style.
As I understand what you want is to modify the "text" of the button, it occurs to me that you can do it this way.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox Text="{Binding ElementName=ButtonTest, Path=Content, UpdateSourceTrigger=PropertyChanged}" ></TextBox>
<Button Name="ButtonTest" Grid.Row="1" Width="100" Height="40">
<Button.ContentTemplate>
<DataTemplate>
<TextBlock Foreground="Blue" Text="{Binding}"></TextBlock>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</Grid>

Access items inside Listview

I have a Listview inside a Listview as you can see in my code. I am trying to collapse other categories when i open another. Is that possible? I have tried many things but I don't know how to access elements in other row...
<ListView x:Name="MainListView"
ItemsSource="{x:Bind menu}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:MainCategories">
<Grid Background="blue">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="test"
Text="{x:Bind CategoryName}"
Tapped="Category_TextBlock_Tapped"
FontSize="25" />
<Grid Grid.Row="1"
Name="tittleGrid"
Background="Gray">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Name"
HorizontalAlignment="Center" />
<Border BorderBrush="Black"
BorderThickness="1,0,1,0"
Grid.Column="1">
<TextBlock HorizontalAlignment="Center"
Text="Price" />
</Border>
<TextBlock Text="QUantity"
Grid.Column="2"
HorizontalAlignment="Center" />
</Grid>
<ListView x:Name="SubListView"
Grid.Row="2"
Background="YellowGreen"
ItemsSource="{x:Bind SubMenuItems}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="Padding"
Value="0" />
<Setter Property="VerticalContentAlignment"
Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Dishes">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center"
Text="{x:Bind dishName}"
HorizontalAlignment="Center" />
<Border BorderBrush="Black"
BorderThickness="1,0,1,0"
Grid.Column="1">
<TextBlock VerticalAlignment="Center"
Grid.Column="1"
HorizontalAlignment="Center"
Text="{x:Bind dishPrice}" />
</Border>
<TextBlock Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{x:Bind dishPrice}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is the toggle-visibility method i have made and works fine for the one i am clicking but I want to collapse ALL the others when i expand the one i click... I might have many mistakes in my code but I'm kinda new to UWP
private void Category_TextBlock_Tapped(Object sender, TappedRoutedEventArgs e)
{
CloseAllOthers();
TextBlock categoryName = sender as TextBlock;
Grid grid = (categoryName.Parent as Grid);
ToggleVisibility(grid);
}
private void ToggleVisibility(Grid grid)
{
foreach (var gr in grid.Children)
{
if (gr.GetType() == grid.GetType() || gr.GetType() == MainListView.GetType())
{
if (gr.Visibility == Visibility.Visible)
{
gr.Visibility = Visibility.Collapsed;
}
else
gr.Visibility = Visibility.Visible;
}
}
}
My result so far
and the collapsed version
Here's a simplified example of what you want to do:
<ListView x:Name="menu" ItemsSource="{x:Bind MenuItems}" IsItemClickEnabled="True" ItemClick="onItemClick">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<!-- Disable virtualization -->
<StackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="MinHeight" Value="0"/>
</Style>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:MenuItem">
<StackPanel>
<TextBlock Text="{x:Bind Text}" FontWeight="Bold" Margin="10,10,0,10"/>
<ListView x:Name="subMenu" ItemsSource="{x:Bind SubItems}" Visibility="Collapsed">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:MenuItem">
<TextBlock Margin="20,5,0,5" Text="{x:Bind Text}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class MenuItem
{
public string Text { get; set; }
public List<MenuItem> SubItems { get; set; }
}
public sealed partial class MainPage : Page
{
public List<MenuItem> MenuItems { get; }
public MainPage()
{
InitializeComponent();
MenuItems = Enumerable.Range(1, 3).Select(i => new MenuItem()
{
Text = $"Menu item {i}",
SubItems = Enumerable.Range(1, 3).Select(j => new MenuItem()
{
Text = $"Sub item {i}.{j}",
}).ToList(),
}).ToList();
}
private void onItemClick(object sender, ItemClickEventArgs e)
{
foreach (var item in menu.Items)
{
var container = (ListViewItem)menu.ContainerFromItem(item);
if (container != null)
{
var subMenu = (container.ContentTemplateRoot as FrameworkElement)?.FindName("subMenu") as FrameworkElement;
if (subMenu != null)
{
subMenu.Visibility = e.ClickedItem == item ? Visibility.Visible : Visibility.Collapsed;
}
}
}
}
}
I wouldn't necessarily recommend this approach; I prefer more of a data-driven approach with bindings to control the visibility of the sub menus when the parent list item is selected, rather than accessing the view directly, but this is fine for simple scenarios.

Multiple DataTemplates for one ListBox

Imagine multiple object types in one Listbox. Both have different look and presented information. If I add any of them in ObservableCollection it displays them fine in ListBox.
<DataTemplate DataType="{x:Type local:DogData}" >
<Grid...
</DataTemplate>
<DataTemplate DataType="{x:Type local:CatData}" >
<Grid...
</DataTemplate>
Now I want users to be able to press a button and switch view to see more detailed information and there templates which provide it
<DataTemplate x:Key="TemplateDogDataWithImages" >
<Grid...
</DataTemplate>
<DataTemplate x:Key="TemplateCatDataWithImages" >
<Grid...
</DataTemplate>
but I can assign only one
AnimalsListBox.ItemTemplate = this.Resources["TemplateDogDataWithImages"] as DataTemplate;
I don't want to have one and have a bunch of triggers in it.
I have researched DataTemplateSelectors
http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector
Is there a better way? Is there a way to switch default templates for each data type so I could avoid DataTemplateSelectors?
On click of the button how do I select TemplateDogDataWithImages and TemplateCatDataWithImages as default ones and cliking other button use TemplateDogDataSimple and TemplateCatDataSimple?
I think a selector is easier (less code), but if you really want to use triggers, maybe something like this?
namespace WpfApplication65
{
public abstract class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public abstract class DataBase : NotifyBase
{
bool m_showDetailed;
public bool ShowDetailed
{
get { return m_showDetailed; }
set
{
m_showDetailed = value;
NotifyPropertyChanged("ShowDetailed");
}
}
}
public class DogData : DataBase { }
public class CatData : DataBase { }
public partial class MainWindow : Window
{
public List<DataBase> Items { get; private set; }
public MainWindow()
{
Items = new List<DataBase>() { new DogData(), new CatData(), new DogData() };
DataContext = this;
InitializeComponent();
}
}
}
<Window x:Class="WpfApplication65.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication65"
Title="MainWindow"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<DataTemplate DataType="{x:Type l:CatData}">
<DataTemplate.Resources>
<DataTemplate x:Key="basic">
<TextBlock Text="This is the basic cat view" />
</DataTemplate>
<DataTemplate x:Key="detailed">
<TextBlock Text="This is the detailed cat view" />
</DataTemplate>
</DataTemplate.Resources>
<Border BorderThickness="1"
BorderBrush="Red"
Margin="2"
Padding="2">
<StackPanel>
<ContentPresenter Name="PART_Presenter"
ContentTemplate="{StaticResource basic}" />
<CheckBox Content="Show Details"
IsChecked="{Binding ShowDetailed}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDetailed}"
Value="True">
<Setter TargetName="PART_Presenter"
Property="ContentTemplate"
Value="{StaticResource detailed}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate DataType="{x:Type l:DogData}">
<DataTemplate.Resources>
<DataTemplate x:Key="basic">
<TextBlock Text="This is the basic dog view" />
</DataTemplate>
<DataTemplate x:Key="detailed">
<TextBlock Text="This is the detailed dog view" />
</DataTemplate>
</DataTemplate.Resources>
<Border BorderThickness="1"
BorderBrush="Blue"
Margin="2"
Padding="2">
<StackPanel>
<ContentPresenter Name="PART_Presenter"
ContentTemplate="{StaticResource basic}" />
<CheckBox Content="Show Details"
IsChecked="{Binding ShowDetailed}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDetailed}"
Value="True">
<Setter TargetName="PART_Presenter"
Property="ContentTemplate"
Value="{StaticResource detailed}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Items}" />
</Window>

Categories

Resources