答案 0 :(得分:0)
您无法从其TreeViewItem
中取出TreeView
。您可以做的最好是修改其模板以水平扩展它。但是,你也不会达到预期的效果,因为扩展区域不会像Popup
那样浮动。
因此,我创建了一个源自Control
的新Menu
。 Menu
最适合您的情况,因为它会水平打开其子菜单。从不离屏。需要新的Control
来覆盖其在mouse-over
上打开子菜单的行为。我们需要TreeView
类似行为,因此必须在mouse-click
上进行扩展,而不是在悬停时进行扩展。
此控件还可以保留全键盘支持,以使用Menu
键导航整个left/right/up/down/esc
。
在您的窗口中删除菜单控件,然后右键单击Menu
中的Document Outline > Edit Additional Templates > ItemContainerStyle
控件。这将为您提供所需的资源。只需复制将其原样粘贴到名为TreeViewFloatingDisplay.xaml
的新文件中,如下所示。我已经省略了...
的简洁代码,替换后的内容会显示注释,新内容会显示在其下方。
TreeViewFloatingDisplay.xaml
<Menu x:Class="WpfStackOverflow.MenuAsTreeView2.TreeViewFloatingDisplay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Menu.Resources>
<Geometry x:Key="RightArrow">M 0,0 L 4,3.5 L 0,7 Z</Geometry>
<LinearGradientBrush x:Key="MenuItemSelectionFill" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#34C5EBFF" Offset="0"/>
<GradientStop Color="#3481D8FF" Offset="1"/>
</LinearGradientBrush>
<Geometry x:Key="Checkmark">M 0,5.1 L 1.7,5.2 L 3.4,7.1 L 8,0.4 L 9.2,0 L 3.3,10.8 Z</Geometry>
<ControlTemplate x:Key="{ComponentResourceKey ResourceId=SubmenuItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="true">
<Rectangle x:Name="Bg" Fill="{TemplateBinding Background}" RadiusY="2" RadiusX="2" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="1"/>
<Rectangle x:Name="InnerBorder" Margin="1" RadiusY="2" RadiusX="2"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="14" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
<!--<ColumnDefinition Width="37"/>-->
<ColumnDefinition Width="0"/>
<ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
<!--<ColumnDefinition Width="17"/>-->
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon" ContentSource="Icon" Margin="1" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<Border x:Name="GlyphPanel" BorderBrush="#CDD3E6" BorderThickness="1" Background="#E6EFF4" CornerRadius="3" Height="22" Margin="1" Visibility="Hidden" Width="22">
<Path x:Name="Glyph" Data="{StaticResource Checkmark}" Fill="#0C12A1" FlowDirection="LeftToRight" Height="11" Width="9"/>
</Border>
<ContentPresenter Grid.Column="2" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<TextBlock Grid.Column="4" Margin="{TemplateBinding Padding}" Text="{TemplateBinding InputGestureText}"/>
</Grid>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
<LinearGradientBrush x:Key="MenuItemPressedFill" EndPoint="0,1" StartPoint="0,0">
...
</LinearGradientBrush>
<SolidColorBrush x:Key="SubMenuBackgroundBrush" Color="#FFF5F5F5"/>
<Geometry x:Key="UpArrow">M 0,4 L 3.5,0 L 7,4 Z</Geometry>
<Style x:Key="MenuScrollButton" BasedOn="{x:Null}" TargetType="{x:Type RepeatButton}">
...
</Style>
<MenuScrollingVisibilityConverter x:Key="MenuScrollingVisibilityConverter"/>
<Geometry x:Key="DownArrow">M 0,0 L 3.5,4 L 7,0 Z</Geometry>
<Style x:Key="{ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}" BasedOn="{x:Null}" TargetType="{x:Type ScrollViewer}">
...
</Style>
<ControlTemplate x:Key="{ComponentResourceKey ResourceId=TopLevelHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="true">
<Rectangle x:Name="OuterBorder" RadiusY="2" RadiusX="2"/>
<Rectangle x:Name="Bg" Fill="{TemplateBinding Background}" Margin="1" RadiusY="1" RadiusX="1" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="1"/>
<Rectangle x:Name="InnerBorder" Margin="2"/>
<DockPanel>
<Path x:Name="PART_RightArrow" Data="{StaticResource RightArrow}" Fill="Transparent" Stroke="Black" Margin="8,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<ContentPresenter Visibility="Collapsed" x:Name="Icon" ContentSource="Icon" Margin="4,0,6,0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<Path x:Name="GlyphPanel" Data="{StaticResource Checkmark}" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="7,0,0,0" Visibility="Collapsed" VerticalAlignment="Center"/>
<ContentPresenter ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</DockPanel>
<!--<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" HorizontalOffset="1" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" VerticalOffset="-1">-->
<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" HorizontalOffset="1" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Right" VerticalOffset="0">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent">
<Border x:Name="SubMenuBorder" BorderBrush="#FF959595" BorderThickness="1" Background="{StaticResource SubMenuBackgroundBrush}">
<ScrollViewer x:Name="SubMenuScrollViewer" Margin="1,0" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{StaticResource SubMenuBackgroundBrush}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
<!-- Comment below 3 rectangles to remove vertical line present on the left -->
<!--<Rectangle Fill="#F1F1F1" HorizontalAlignment="Left" Margin="1,2" RadiusY="2" RadiusX="2" Width="28"/>
<Rectangle Fill="#E2E3E3" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
<Rectangle Fill="White" HorizontalAlignment="Left" Margin="30,2,0,2" Width="1"/>-->
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="true" Margin="2" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="{ComponentResourceKey ResourceId=TopLevelItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
...
</ControlTemplate>
<ControlTemplate x:Key="{ComponentResourceKey ResourceId=SubmenuHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="true">
<Rectangle x:Name="Bg" Fill="{TemplateBinding Background}" RadiusY="2" RadiusX="2" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="1"/>
<Rectangle x:Name="InnerBorder" Margin="1" RadiusY="2" RadiusX="2" Stroke="Transparent" StrokeThickness="1"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="14" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
<!--<ColumnDefinition Width="37"/>-->
<ColumnDefinition Width="0"/>
<ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
<!--<ColumnDefinition Width="17"/>-->
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon" ContentSource="Icon" Margin="1" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<Border x:Name="GlyphPanel" BorderBrush="#CDD3E6" BorderThickness="1" Background="#E6EFF4" CornerRadius="3" Height="22" Margin="1" Visibility="Hidden" Width="22">
<Path x:Name="Glyph" Data="{StaticResource Checkmark}" Fill="#0C12A1" FlowDirection="LeftToRight" Height="11" Width="9"/>
</Border>
<ContentPresenter Grid.Column="2" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<TextBlock Grid.Column="4" Margin="{TemplateBinding Padding}" Text="{TemplateBinding InputGestureText}" Visibility="Collapsed"/>
<!--<Path Grid.Column="5" Data="{StaticResource RightArrow}" Fill="{TemplateBinding Foreground}" Margin="4,0,0,0" VerticalAlignment="Center"/>-->
<Path Grid.Column="0" x:Name="PART_RightArrow" Data="{StaticResource RightArrow}" Fill="Transparent" Stroke="Black" Margin="4,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
<!--<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" HorizontalOffset="-2" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Right" VerticalOffset="-3">-->
<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" HorizontalOffset="3" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Right" VerticalOffset="-4">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent">
<Border x:Name="SubMenuBorder" BorderBrush="#FF959595" BorderThickness="1" Background="{StaticResource SubMenuBackgroundBrush}">
<ScrollViewer x:Name="SubMenuScrollViewer" Margin="1,0" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{StaticResource SubMenuBackgroundBrush}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
<!-- Comment below 3 rectangles to remove vertical line present on the left -->
<!--<Rectangle Fill="#F1F1F1" HorizontalAlignment="Left" Margin="1,2" RadiusY="2" RadiusX="2" Width="28"/>
<Rectangle Fill="#E2E3E3" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
<Rectangle Fill="White" HorizontalAlignment="Left" Margin="30,2,0,2" Width="1"/>-->
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="true" Margin="2" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="MenuItemStyle1" TargetType="{x:Type MenuItem}">
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=SubmenuItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
<Style.Triggers>
<Trigger Property="Role" Value="TopLevelHeader">
<Setter Property="Padding" Value="7,2,8,3"/>
<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
</Trigger>
<Trigger Property="Role" Value="TopLevelItem">
<Setter Property="Padding" Value="7,2,8,3"/>
<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
</Trigger>
<Trigger Property="Role" Value="SubmenuHeader">
<!--<Setter Property="Padding" Value="2,3,2,3"/>-->
<Setter Property="Padding" Value="1,2,1,2"/>
<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=SubmenuHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
</Trigger>
<Trigger Property="Role" Value="SubmenuItem">
<!--<Setter Property="Padding" Value="2,3,2,3"/>-->
<Setter Property="Padding" Value="1,2,1,2"/>
</Trigger>
</Style.Triggers>
</Style>
</Menu.Resources>
</Menu>
TreeViewFloatingDisplay.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace WpfStackOverflow.MenuAsTreeView2
{
/// <summary>
/// Interaction logic for TreeViewFloatingDisplay.xaml
/// </summary>
public partial class TreeViewFloatingDisplay : Menu
{
public TreeViewFloatingDisplay()
{
InitializeComponent();
}
protected override DependencyObject GetContainerForItemOverride()
{
return new MenuItemNew();
}
}
public class MenuItemNew : MenuItem
{
#region Constructors
static MenuItemNew()
{
MenuItem.IsSubmenuOpenProperty.OverrideMetadata(typeof(MenuItemNew),
new FrameworkPropertyMetadata(false, MenuItem.IsSubmenuOpenProperty.DefaultMetadata.PropertyChangedCallback, CoerceIsSubmenuOpen));
}
public MenuItemNew()
{
this.PreviewMouseLeftButtonDown += MenuItemNew_PreviewMouseLeftButtonDown;
}
#endregion
#region Event Handlers
void MenuItemNew_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MenuItemNew curItem = sender as MenuItemNew;
System.Diagnostics.Debug.WriteLine("Role = " + curItem.Role);
Path pth = e.OriginalSource as Path;
if (pth == null) return;
DependencyObject parent = VisualTreeHelper.GetParent(pth);
while (parent as MenuItemNew == null)
parent = VisualTreeHelper.GetParent(parent);
MenuItemNew actualSource = parent as MenuItemNew;
if (actualSource.Role == MenuItemRole.TopLevelHeader)
{
IsExpanderClicked = !IsExpanderClicked;
actualSource.CoerceValue(MenuItemNew.IsSubmenuOpenProperty);
}
if (curItem.Role == MenuItemRole.SubmenuHeader)
{
IsExpanderClicked = !IsExpanderClicked;
curItem.CoerceValue(MenuItemNew.IsSubmenuOpenProperty);
}
}
#endregion
#region IsExpanderClicked Dependency Property
public bool IsExpanderClicked
{
get { return (bool)GetValue(IsExpanderClickedProperty); }
set { SetValue(IsExpanderClickedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsExpanderClicked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsExpanderClickedProperty =
DependencyProperty.Register("IsExpanderClicked", typeof(bool), typeof(MenuItemNew), new PropertyMetadata(false));
#endregion
#region Overrides
protected override DependencyObject GetContainerForItemOverride()
{
return new MenuItemNew();
}
#endregion
#region Dependency Property Callbacks
private static object CoerceIsSubmenuOpen(DependencyObject d, object value)
{
MenuItemNew item = (MenuItemNew)d;
if ((bool)value)
{
if (item.IsMouseOver)
{
System.Diagnostics.Debug.WriteLine(item.IsExpanderClicked.ToString());
return item.IsExpanderClicked;
}
if (!item.IsLoaded)
{
item.Loaded += (s, e) =>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param)
{
item.CoerceValue(MenuItemNew.IsSubmenuOpenProperty);
return null;
}), null);
};
return false;
}
}
if (item.IsExpanderClicked)
return true;
return value;
}
#endregion
}
}
用法的
<Grid>
<myMenu:TreeViewFloatingDisplay Background="AliceBlue" ItemsSource="{Binding Countries}" Width="300" HorizontalAlignment="Left">
<myMenu:TreeViewFloatingDisplay.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</myMenu:TreeViewFloatingDisplay.ItemsPanel>
<myMenu:TreeViewFloatingDisplay.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding States}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Cities}">
<Grid>
<ContentControl Content="{Binding .}">
<ContentControl.ContentTemplate>
<DataTemplate>
<Grid Background="#FFE8E8E8" HorizontalAlignment="Stretch" MinHeight="35">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="75"/>
<ColumnDefinition MinWidth="150"/>
<ColumnDefinition MinWidth="30"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="3 1 1 1" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBox Grid.Column="1" Margin="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" />
<Label Grid.Column="2" Margin="1" VerticalAlignment="Center">
<CheckBox />
</Label>
</Grid>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<ContentControl Content="{Binding .}">
<ContentControl.ContentTemplate>
<DataTemplate>
<Grid Background="#FFE8E8E8" HorizontalAlignment="Stretch" Width="255" MinHeight="35">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="1" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBox Grid.Column="1" Margin="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" />
<Label Grid.Column="2" Margin="1" VerticalAlignment="Center">
<CheckBox />
</Label>
</Grid>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</HierarchicalDataTemplate>
</myMenu:TreeViewFloatingDisplay.ItemTemplate>
</myMenu:TreeViewFloatingDisplay>
</Grid>
ViewModel.cs
public class ViewModel
{
public ObservableCollection<Country> Countries { get; set; }
public ViewModel()
{
Countries = new ObservableCollection<Country>();
Countries.Add(new Country()
{
Name = "India",
States = new ObservableCollection<State>()
{ new State()
{
Name="MP",
Cities = new ObservableCollection<City>()
{
new City(){Name = "Bhopal"},new City(){Name = "Indore"}, new City(){Name = "Ujjain"}
}
} ,
new State()
{
Name="UP",
Cities = new ObservableCollection<City>()
{
new City(){Name = "Agra"},new City(){Name = "Noida"}, new City(){Name = "Jhansi"}
}
} ,
new State()
{
Name="AP",
Cities = new ObservableCollection<City>()
{
new City(){Name = "Hyderabad"},new City(){Name = "Warangal"}, new City(){Name = "Vijaywada"}
}
}
}
});
Countries.Add(new Country()
{
Name = "Australia",
States = new ObservableCollection<State>()
{ new State()
{
Name="Tasmania",
Cities = new ObservableCollection<City>()
{
new City(){Name = "Sydney"},new City(){Name = "Melbourne"}
}
}
}
});
}
}
public class Country
{
public string Name { get; set; }
public ObservableCollection<State> States { get; set; }
}
public class State
{
public string Name { get; set; }
public ObservableCollection<City> Cities { get; set; }
}
public class City
{
public string Name { get; set; }
}