C
C#Denis

❔ [AvaloniaUI] Visibility of item in ListView based on condition

An Avalonia UI application is used to manage an evidence of items. It allows the user to add items. or remove them. The View displays a list of items in a ListView. The Item model represents an item in the evidence. The ViewModel for the View handles retrieving the list of Item instances. The View binds the observable list of Item instances to the ListView. The goal is to show the Remove button as part of a ListViewItem, but only when that given item in the ListView is selected. My idea is to only handle the collection of Item instances and the currently selected Item in the ViewModel, while the View would do the rest. I wanted to accomplish it via a Converter, that would receive the templated Item and the currently selected Item. However, after fighting with it for some time, I was unable to make it work. Is my thinking correct - should the View be responsible for handling the Remove button visibility? Should it be done in the ViewModel?
D
Denis398d ago
// Model
public partial class Item
: ObservableObject
{
[ObservableProperty]
private Guid _id = Guid.Empty;

[ObservableProperty]
private string _name = string.Empty;

[ObservableProperty]
private string _notes = string.Empty;
}
// Model
public partial class Item
: ObservableObject
{
[ObservableProperty]
private Guid _id = Guid.Empty;

[ObservableProperty]
private string _name = string.Empty;

[ObservableProperty]
private string _notes = string.Empty;
}
/// ViewModel
public partial class MainWindowViewModel
: ObservableObject
{
private Item? m_selectedItem;

public Item? SelectedItem
{
get => m_selectedItem;
set
{
if (Equals(value, m_selectedItem)) return;
m_selectedItem = value;
OnPropertyChanged();
DeleteItemCommand.NotifyCanExecuteChanged();
}
}

/// <summary>
/// Evidence of items
/// </summary>
public ObservableCollection<Item> Items { get; set; }

/// <summary>
/// Default constructor
/// </summary>
public MainWindowViewModel()
{
var items = new Faker<Item>()
.RuleFor(item => item.Id, Guid.NewGuid)
.RuleFor(item => item.Name, faker => faker.Name.FullName())
.RuleFor(item => item.Notes, string.Empty)
.Generate(10);

Items = new ObservableCollection<Item>(items);
}

[RelayCommand(CanExecute = nameof(CanDeleteItem))]
private void DeleteItem()
{
Students.Remove(SelectedItem);
}

private bool CanDeleteItem() => SelectedItem is not null;
}
/// ViewModel
public partial class MainWindowViewModel
: ObservableObject
{
private Item? m_selectedItem;

public Item? SelectedItem
{
get => m_selectedItem;
set
{
if (Equals(value, m_selectedItem)) return;
m_selectedItem = value;
OnPropertyChanged();
DeleteItemCommand.NotifyCanExecuteChanged();
}
}

/// <summary>
/// Evidence of items
/// </summary>
public ObservableCollection<Item> Items { get; set; }

/// <summary>
/// Default constructor
/// </summary>
public MainWindowViewModel()
{
var items = new Faker<Item>()
.RuleFor(item => item.Id, Guid.NewGuid)
.RuleFor(item => item.Name, faker => faker.Name.FullName())
.RuleFor(item => item.Notes, string.Empty)
.Generate(10);

Items = new ObservableCollection<Item>(items);
}

[RelayCommand(CanExecute = nameof(CanDeleteItem))]
private void DeleteItem()
{
Students.Remove(SelectedItem);
}

private bool CanDeleteItem() => SelectedItem is not null;
}
<Window
Icon="/Assets/avalonia-logo.ico"
Name="Root"
Title="TestApp"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="TestApp.Views.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:converters="clr-namespace:TestApp.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:TestApp.Models"
xmlns:vm="using:TestApp.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Design.DataContext>
<vm:MainWindowViewModel />
</Design.DataContext>
<Grid ColumnDefinitions="Auto,*">
<ListBox
Grid.Column="0"
Items="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
SelectionMode="Single">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type models:Item}">
<Grid ColumnDefinitions="*,Auto">
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<Button
Command="{Binding DeleteItemCommand, ElementName=Root}"
Grid.Column="1"
Margin="5,0,0,0">
Delete
<Button.IsVisible>
<Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType=Window}">
<Binding.Converter>
<converters:SelectedItemToBoolConverter />
</Binding.Converter>
</Binding>
</Button.IsVisible>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
<Window
Icon="/Assets/avalonia-logo.ico"
Name="Root"
Title="TestApp"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="TestApp.Views.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:converters="clr-namespace:TestApp.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:TestApp.Models"
xmlns:vm="using:TestApp.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Design.DataContext>
<vm:MainWindowViewModel />
</Design.DataContext>
<Grid ColumnDefinitions="Auto,*">
<ListBox
Grid.Column="0"
Items="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
SelectionMode="Single">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type models:Item}">
<Grid ColumnDefinitions="*,Auto">
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<Button
Command="{Binding DeleteItemCommand, ElementName=Root}"
Grid.Column="1"
Margin="5,0,0,0">
Delete
<Button.IsVisible>
<Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType=Window}">
<Binding.Converter>
<converters:SelectedItemToBoolConverter />
</Binding.Converter>
</Binding>
</Button.IsVisible>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
// Converter
public class SelectedItemToBoolConverter
: AvaloniaObject, IValueConverter
{
public static readonly StyledProperty<object?> SelectedItemProperty
= AvaloniaProperty.Register<SelectedItemToBoolConverter, object?>(nameof(SelectedItem));

public object? SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}

/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is Item itm && SelectedItem is Item currentItm && itm.Id.Equals(currentItm.Id);

/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotImplementedException();
}
// Converter
public class SelectedItemToBoolConverter
: AvaloniaObject, IValueConverter
{
public static readonly StyledProperty<object?> SelectedItemProperty
= AvaloniaProperty.Register<SelectedItemToBoolConverter, object?>(nameof(SelectedItem));

public object? SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}

/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is Item itm && SelectedItem is Item currentItm && itm.Id.Equals(currentItm.Id);

/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotImplementedException();
}
I confess, that the current converter implementation and use is incorrect; however, I wish to hear some of your advice, before I continue mashing my head against the wall to find the correct solution. The main question is whether my approach is even correct
A
Accord397d ago
Looks like nothing has happened here. I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server
More Posts
✅ How to structure this login/signup page layoutSo I have this mockup of the layout I want for a login/signup page in my Blazor WASM app. Pretty sta❔ I am need of help before I give up and run my code on a server instead of a serverless solution.I have create an azure function locally and i've used the selenuim webdriver package for taking scre✅ .Net Core 6 Asymmetric Encryption with custom public and private keysHello all! How can i use the `Asymmetric Encryption` in .Net 6 but choosing/importing private and pu❔ No Design in C#Instances of this bug (1) 1. View Call Stack at System.Runtime.InteropServices.Marshal.ThrowExce❔ having issues on a reloading script on unity to reload weaponsthe code is used in a youtube video and i have pretty much copied to to get it to work but it doesnt❔ help with an exerciseI've encountered a weird problem that idk how to fix. Say we've got a string "31131123521" how do i ❔ Accessing HTTP Context at DbCommandInterceptor [.NET 7]Hi! I'm having some issues trying to access the HTTP Context at my DbCommand Interceptor class. What❔ XMLAttributeCollection -> Dictionary with LINQ?It's a confusing class. It only allows turning it into a Queryable, but I have no experience with th❔ MS SQL Reporting Server URL Being RedirectedWe have an application from a vendor that was written in C# and we recently upgraded the MS SQL data❔ dataset memory leakI found memory leak and I don't understand how to fix it Test case: ```cpp public class MemoryLeaksT❔ WebAPI .NET 7.0 - Encrypt connectionstring in appsettings.jsonguys, I'm writing some WebAPI using .NET 7.0, and I'd like to secure the connectionstring with encry❔ how to read strings correctly with System.Data.SQLitei m trying read turkish characters from database but i can't see correctly❔ Blazor server app, base url / base page to set environmentHi 🙂 I have a Blazor Server app, where I would like to use some dynamic baseurl. Say instead of my❔ C# Console Application -its only writing the same random string, how can i make it do different ones?