//D:\C\WpfApp14\Themes\Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp14">
<Style TargetType="{x:Type local:StateCountyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:StateCountyControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="PART_StateLbx"
Grid.Column="0"
BorderBrush="LightBlue"
BorderThickness="2"
Margin="5"
SelectedItem="{Binding SelectedState,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="FontSize" Value="30"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="ExtraBold"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
</ListBox>
<DataGrid x:Name="PART_CountyDG"
Grid.Column="1"
BorderBrush="LightBlue"
BorderThickness="2"
Margin="5"
ItemsSource="{Binding CountyCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.CacheLengthUnit="Item"
VirtualizingPanel.CacheLength="5,5"
UseLayoutRounding="True"
ScrollViewer.CanContentScroll="True"
ScrollViewer.IsDeferredScrollingEnabled="True"
IsReadOnly="True"
SelectionMode="Extended">
<DataGrid.ItemContainerStyle>
<Style TargetType="DataGridRow">
<Setter Property="FontSize" Value="30"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.ItemContainerStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="ExtraBold"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
</DataGrid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
//using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
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 WpfApp14
{
/// <summary>
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
///
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:WpfApp14"
///
///
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:WpfApp14;assembly=WpfApp14"
///
/// You will also need to add a project reference from the project where the XAML file lives
/// to this project and Rebuild to avoid compilation errors:
///
/// Right click on the target project in the Solution Explorer and
/// "Add Reference"->"Projects"->[Browse to and select this project]
///
///
/// Step 2)
/// Go ahead and use your control in the XAML file.
///
/// <MyNamespace:StateCountyControl/>
///
/// </summary>
public class StateCountyControl : Control
{
private ListBox stateLbx;
private DataGrid countyDg;
static StateCountyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(StateCountyControl), new FrameworkPropertyMetadata(typeof(StateCountyControl)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var tempStateLbx = GetTemplateChild("PART_StateLbx") as ListBox;
if (tempStateLbx != null)
{
stateLbx = tempStateLbx;
stateLbx.ItemsSource = StatesCollection;
stateLbx.SelectedItem = SelectedState;
}
var tempDG = GetTemplateChild("PART_CountyDG") as DataGrid;
if(tempDG!=null)
{
countyDg = tempDG;
countyDg.ItemsSource = CountyCollection;
}
}
public ObservableCollection<string> StatesCollection
{
get { return (ObservableCollection<string>)GetValue(StatesCollectionProperty); }
set { SetValue(StatesCollectionProperty, value); }
}
// Using a DependencyProperty as the backing store for StatesCollection. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StatesCollectionProperty =
DependencyProperty.Register(nameof(StatesCollection),
typeof(ObservableCollection<string>),
typeof(StateCountyControl),
new PropertyMetadata(null));
public string SelectedState
{
get { return (string)GetValue(SelectedStateProperty); }
set { SetValue(SelectedStateProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedState. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedStateProperty =
DependencyProperty.Register(nameof(SelectedState),
typeof(string),
typeof(StateCountyControl),
new PropertyMetadata(null));
public ObservableCollection<CountyModel> CountyCollection
{
get { return (ObservableCollection<CountyModel>)GetValue(CountyCollectionProperty); }
set { SetValue(CountyCollectionProperty, value); }
}
// Using a DependencyProperty as the backing store for CountyCollection. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CountyCollectionProperty =
DependencyProperty.Register(nameof(CountyCollection),
typeof(ObservableCollection<CountyModel>),
typeof(StateCountyControl),
new PropertyMetadata(null));
}
public class CountyModel
{
public string County { get; set; }
public string County_Ascii { get; set; }
public string County_Full { get; set; }
public string County_Fips { get; set; }
public string State_Name { get; set; }
public double Lat { get; set; }
public string Lng { get; set; }
public long Population { get; set; }
}
}
//D:\C\WpfApp14\MainWindow.xaml
<Window x:Class="WpfApp14.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:WpfApp14"
mc:Ignorable="d"
Title="{Binding MainTitle}" WindowState="Maximized">
<Window.DataContext>
<local:MainVM/>
</Window.DataContext>
<Grid>
<local:StateCountyControl
StatesCollection="{Binding StatesCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedState="{Binding SelectedState,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
CountyCollection="{Binding CountyCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
//D:\C\WpfApp14\MainWindow.xaml.cs
using CsvHelper;
using CsvHelper.Configuration;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
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 WpfApp14
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MainVM : INotifyPropertyChanged
{
private List<CountyModel> countyModelsList;
public MainVM()
{
if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
string csvFile = @"../../../uscounties.csv";
ParsetCSVFileToList(csvFile);
}
}
private void ParsetCSVFileToList(string csvFile)
{
if (!File.Exists(csvFile))
{
return;
}
string csvStr = File.ReadAllText(csvFile);
if (string.IsNullOrWhiteSpace(csvStr))
{
return;
}
try
{
using (StringReader sr = new StringReader(csvStr))
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
PrepareHeaderForMatch = args => args.Header.ToLower(),
MissingFieldFound = null,
HeaderValidated = null
};
using (var reader = new CsvReader(sr, config))
{
countyModelsList = reader.GetRecords<CountyModel>().ToList();
HashSet<string> statesSet = new HashSet<string>();
StatesCollection = new ObservableCollection<string>(countyModelsList.Select(x=>x.State_Name).OrderBy(x=>x).ToList().Distinct());
SelectedState = StatesCollection.FirstOrDefault();
CountyCollection = new ObservableCollection<CountyModel>(countyModelsList.Where(x => string.Equals(x.State_Name, selectedState, StringComparison.InvariantCultureIgnoreCase)).ToList());
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex?.Message);
}
}
private ObservableCollection<string> statesCollection;
public ObservableCollection<string> StatesCollection
{
get
{
return statesCollection;
}
set
{
if (value != statesCollection)
{
statesCollection = value;
OnPropertyChanged();
}
}
}
private string selectedState;
public string SelectedState
{
get
{
return selectedState;
}
set
{
if(value!=selectedState)
{
selectedState = value;
OnPropertyChanged();
OnSelectedStateChanged();
}
}
}
private void OnSelectedStateChanged()
{
CountyCollection = new ObservableCollection<CountyModel>(countyModelsList.Where(x => string.Equals(x.State_Name, SelectedState, StringComparison.InvariantCultureIgnoreCase)).OrderBy(x=>x.County).ToList());
MainTitle = $"Selected state is {SelectedState}";
}
private string mainTitle;
public string MainTitle
{
get
{
return mainTitle;
}
set
{
if(value!=mainTitle)
{
mainTitle = value;
OnPropertyChanged();
}
}
}
private ObservableCollection<CountyModel> countyCollection;
public ObservableCollection<CountyModel> CountyCollection
{
get
{
return countyCollection;
}
set
{
if(value!=countyCollection)
{
countyCollection = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "")
{
var handler = Volatile.Read(ref PropertyChanged);
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(propName));
}
}
}
![image]()
![image]()
![image]()
![image]()