-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add AdvancedGrid control and searchable selection list
Renamed `Grid` to `AdvancedGrid` and refactored it to utilize a `UserControl` with enhanced methods. Introduced a `SearchableSelectionList` control, which allows searching and selecting items. Updated `TextInput` to handle text change events more appropriately and added tests for the new components.
- Loading branch information
1 parent
04d1b10
commit 405b8b5
Showing
24 changed files
with
524 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using System.Windows; | ||
using System.Windows.Controls; | ||
|
||
namespace Frank.Wpf.Controls.Grid; | ||
|
||
public class AdvancedGrid : UserControl | ||
{ | ||
private readonly Cell[,] _cells; | ||
private readonly System.Windows.Controls.Grid _grid = new(); | ||
|
||
public AdvancedGrid(int columns, int rows) | ||
{ | ||
_cells = new Cell[columns, rows]; | ||
|
||
for (var i = 0; i < columns; i++) _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto }); | ||
for (var i = 0; i < rows; i++) _grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto }); | ||
|
||
for (var i = 0; i < columns; i++) | ||
for (var j = 0; j < rows; j++) | ||
_cells[i, j] = new Cell(new CellPosition(i, j)); | ||
|
||
foreach (var cell in _cells) _grid.Children.Add(cell); | ||
} | ||
|
||
/// <summary> | ||
/// Clears the grid of all content. | ||
/// </summary> | ||
public void Clear() => _grid.Children.Clear(); | ||
|
||
/// <summary> | ||
/// Gets the underlying grid's UI element collection. | ||
/// </summary> | ||
/// <returns></returns> | ||
public UIElementCollection GetUIElementCollection() => _grid.Children; | ||
|
||
/// <summary> | ||
/// Gets the UI elements in the grid. | ||
/// </summary> | ||
/// <returns></returns> | ||
public IEnumerable<UIElement> GetUIElements() => _grid.Children.Cast<UIElement>(); | ||
|
||
/// <summary> | ||
/// Sets the content of the cell at the specified position. | ||
/// </summary> | ||
/// <param name="position"></param> | ||
/// <param name="content"></param> | ||
/// <typeparam name="T"></typeparam> | ||
public void SetCellContent<T>(CellPosition position, T content) where T : UIElement => _cells[position.Column, position.Row].Content = content; | ||
|
||
/// <summary> | ||
/// Gets the content of the cell at the specified position. | ||
/// </summary> | ||
/// <param name="position"></param> | ||
/// <typeparam name="T"></typeparam> | ||
/// <returns></returns> | ||
public T GetCellContent<T>(CellPosition position) where T : UIElement => (T)_cells[position.Column, position.Row].Content; | ||
|
||
/// <summary> | ||
/// Gets the content of the cell at the specified position without knowing the type. | ||
/// </summary> | ||
/// <param name="position"></param> | ||
/// <returns></returns> | ||
public object GetCellContent(CellPosition position) => _cells[position.Column, position.Row].Content; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
namespace Frank.Wpf.Controls.Grid; | ||
|
||
public readonly record struct CellPosition(int Column, int Row); | ||
/// <summary> | ||
/// Describes the position of a cell in a grid with optional spans. | ||
/// </summary> | ||
/// <param name="Column"></param> | ||
/// <param name="Row"></param> | ||
/// <param name="ColumnSpan"></param> | ||
/// <param name="RowSpan"></param> | ||
public readonly record struct CellPosition(int Column, int Row, int ColumnSpan = 1, int RowSpan = 1); |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
Frank.Wpf.Controls.SearchableList/Frank.Wpf.Controls.SearchableList.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
|
||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Frank.Wpf.Controls.SimpleInputs\Frank.Wpf.Controls.SimpleInputs.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# SearchableSelectionList<T> | ||
|
||
A control that allows the user to search for an item in a list of items. The list of items is displayed in a dropdown list. The user can type in the search box to filter the list of items. The user can also use the up and down arrow keys to navigate through the list of items. | ||
|
||
## Usage | ||
|
||
```csharp | ||
|
||
``` | ||
|
||
|
||
## License | ||
|
||
![License](https://img.shields.io/github/license/frankhaugen/Frank.Wpf) |
93 changes: 93 additions & 0 deletions
93
Frank.Wpf.Controls.SearchableList/SearchableSelectionList.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
using System.Windows; | ||
using System.Windows.Controls; | ||
using Frank.Wpf.Controls.SimpleInputs; | ||
|
||
namespace Frank.Wpf.Controls.SearchableList; | ||
|
||
/// <summary> | ||
/// A list that allows searching and selecting items. | ||
/// </summary> | ||
/// <typeparam name="T"></typeparam> | ||
public sealed class SearchableSelectionList<T> : UserControl | ||
{ | ||
private readonly StackPanel _stackPanel = new(); | ||
private readonly SearchBox _searchBox; | ||
private readonly GroupBox _groupBox = new() { Header = "Selected" }; | ||
private readonly ScrollViewer _scrollViewer = new(); | ||
private readonly CustomListBox<T> _listBox = new(); | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="SearchableSelectionList{T}"/>. | ||
/// </summary> | ||
public SearchableSelectionList() | ||
{ | ||
_searchBox = new SearchBox("Search", x => | ||
{ | ||
if (DisplayFunc != null) | ||
{ | ||
_listBox.FilterFunc = item => DisplayFunc(item) | ||
.Contains(x.SearchText ?? string.Empty, StringComparison.InvariantCultureIgnoreCase); | ||
} | ||
}); | ||
|
||
_scrollViewer.Content = _listBox; | ||
_stackPanel.Children.Add(_searchBox); | ||
_stackPanel.Children.Add(_scrollViewer); | ||
_stackPanel.Children.Add(_groupBox); | ||
|
||
MinWidth = 128; | ||
Content = _stackPanel; | ||
} | ||
|
||
/// <summary> | ||
/// Determines if the search box is visible. Default is true. | ||
/// </summary> | ||
public bool IsSearchBoxVisible | ||
{ | ||
get => _searchBox.Visibility == Visibility.Visible; | ||
set => _searchBox.Visibility = value ? Visibility.Visible : Visibility.Collapsed; | ||
} | ||
|
||
/// <summary> | ||
/// Determines if the selected item box is visible. Default is true. | ||
/// </summary> | ||
public bool IsSelectedBoxVisible | ||
{ | ||
get => _groupBox.Visibility == Visibility.Visible; | ||
set => _groupBox.Visibility = value ? Visibility.Visible : Visibility.Collapsed; | ||
} | ||
|
||
/// <summary> | ||
/// The items in the list. | ||
/// </summary> | ||
public required IEnumerable<T> Items | ||
{ | ||
get => _listBox.Items; | ||
set => _listBox.Items = value; | ||
} | ||
|
||
/// <summary> | ||
/// The function that determines how to display an item. | ||
/// </summary> | ||
/// <remarks> Search functionality is based on this function. </remarks> | ||
public required Func<T, string> DisplayFunc | ||
{ | ||
get => _listBox.DisplayFunc; | ||
init => _listBox.DisplayFunc = value; | ||
} | ||
|
||
/// <summary> | ||
/// This action is called when an item is selected. | ||
/// </summary> | ||
public required Action<T> SelectionChangedAction | ||
{ | ||
init | ||
{ | ||
_listBox.SelectionChangedAction = item => | ||
{ | ||
_groupBox.Content = new TextBlock { Text = DisplayFunc(item) }; | ||
value?.Invoke(item); | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
using System.Windows; | ||
using System.Windows.Controls; | ||
|
||
namespace Frank.Wpf.Controls.SimpleInputs; | ||
|
||
public class CustomListBox<T> : UserControl | ||
{ | ||
private readonly ListBox _listBox; | ||
|
||
public CustomListBox() | ||
{ | ||
_listBox = new ListBox(); | ||
Content = _listBox; | ||
|
||
_listBox.SelectionChanged += ListBox_SelectionChanged; | ||
} | ||
|
||
public static readonly DependencyProperty ItemsProperty = | ||
DependencyProperty.Register( | ||
nameof(Items), | ||
typeof(IEnumerable<T>), | ||
typeof(CustomListBox<T>), | ||
new PropertyMetadata(new List<T>(), OnItemsChanged)); | ||
|
||
public static readonly DependencyProperty DisplayFuncProperty = | ||
DependencyProperty.Register( | ||
nameof(DisplayFunc), | ||
typeof(Func<T, string>), | ||
typeof(CustomListBox<T>), | ||
new PropertyMetadata(null, OnDisplayFuncChanged)); | ||
|
||
public static readonly DependencyProperty FilterFuncProperty = | ||
DependencyProperty.Register( | ||
nameof(FilterFunc), | ||
typeof(Func<T, bool>), | ||
typeof(CustomListBox<T>), | ||
new PropertyMetadata(null, OnFilterFuncChanged)); | ||
|
||
public static readonly DependencyProperty SelectionChangedActionProperty = | ||
DependencyProperty.Register( | ||
nameof(SelectionChangedAction), | ||
typeof(Action<T>), | ||
typeof(CustomListBox<T>), | ||
new PropertyMetadata(null)); | ||
|
||
public IEnumerable<T> Items | ||
{ | ||
get => (IEnumerable<T>)GetValue(ItemsProperty); | ||
set => SetValue(ItemsProperty, value); | ||
} | ||
|
||
public Func<T, string> DisplayFunc | ||
{ | ||
get => (Func<T, string>)GetValue(DisplayFuncProperty); | ||
set => SetValue(DisplayFuncProperty, value); | ||
} | ||
|
||
public Func<T, bool> FilterFunc | ||
{ | ||
get => (Func<T, bool>)GetValue(FilterFuncProperty); | ||
set => SetValue(FilterFuncProperty, value); | ||
} | ||
|
||
public Action<T> SelectionChangedAction | ||
{ | ||
get => (Action<T>)GetValue(SelectionChangedActionProperty); | ||
set => SetValue(SelectionChangedActionProperty, value); | ||
} | ||
|
||
private static void OnItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | ||
{ | ||
var control = (CustomListBox<T>)d; | ||
control.ApplyFilter(); | ||
} | ||
|
||
private static void OnDisplayFuncChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | ||
{ | ||
var control = (CustomListBox<T>)d; | ||
control.ApplyFilter(); | ||
} | ||
|
||
private static void OnFilterFuncChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | ||
{ | ||
var control = (CustomListBox<T>)d; | ||
control.ApplyFilter(); | ||
} | ||
|
||
private void ApplyFilter() | ||
{ | ||
_listBox.Items.Clear(); | ||
|
||
if (Items == null || DisplayFunc == null) | ||
{ | ||
return; | ||
} | ||
|
||
var filteredItems = FilterFunc != null ? Items.Where(FilterFunc) : Items; | ||
|
||
foreach (var item in filteredItems) | ||
{ | ||
_listBox.Items.Add(new ListBoxItem { Content = DisplayFunc(item), Tag = item }); | ||
} | ||
} | ||
|
||
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) | ||
{ | ||
if (_listBox.SelectedItem is ListBoxItem selectedItem && SelectionChangedAction != null) | ||
{ | ||
SelectionChangedAction((T)selectedItem.Tag); | ||
} | ||
} | ||
} |
Oops, something went wrong.