Как передать значимый тип в ViewModel что бы при изменении его, в ViewModel он изменялся тоже и срабатывал binding?
Я реализую в своем приложении выпадающее меню, в котором находится список кнопок. Через ItemsControl и класс ActionButton(String ImgUrl, string Text) я отрисовываю список в xaml.
Условно в ViewModel выпадающего меню мы передаем некоторые классы с INotifyPropertyChanged И допустим нужно сделать такую возможность, что бы когда в классе с INotifyPropertyChanged изменялось некоторое свойство, то кнопка могла отреагировать на это и поменять текст на дополнительный(Или любой заданный другой)
Для реализации INotifyPropertyChanged я использую библиотеку CommunityToolkit.Mvvm.ComponentModel
Перейдем немного к коду:
public partial class DropdownMenuViewModel : ObservableObject
{
public Server Server;
public Profile Profile;
public ObservableCollection<ActionButton> Buttons { get; set; }
public DropdownMenuViewModel(Server server, Profile profile)
{
Server = server;
Profile = profile;
Buttons = new ObservableCollection<ActionButton>
{
new ActionButton("Img", "Включить сервер", Server.IsEnabled, "Выключить сервер"),
new ActionButton("Img", "Войти", Profile.IsOnline, "Выйти"),
};
}
}
public partial class Server : ObservableObject
{
[ObservableProperty]
private bool _isEnabled;
}
public partial class Profile : ObservableObject
{
[ObservableProperty]
private bool _isOnline;
}
public class ActionButton(string imageUrl, string text, bool? condition = null, string? additionalText = null)
{
public string ImageUrl { get; set; } = imageUrl;
public string Text { get; set; } = text;
public bool? Condition { get; set; } = condition;
public string? AdditionalText { get; set; } = additionalText;
}
И код UserControl:
<Grid>
<ItemsControl Width="150" ItemsSource="{Binding Buttons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Background="Aqua">
<TextBlock Text="{Binding ImageUrl}" />
<TextBlock HorizontalAlignment="Left">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Condition}" Value="{x:Null}">
<Setter Property="Text" Value="{Binding Text}" />
</DataTrigger>
<DataTrigger Binding="{Binding Condition}" Value="False">
<Setter Property="Text" Value="{Binding Text}" />
</DataTrigger>
<DataTrigger Binding="{Binding Condition}" Value="True">
<Setter Property="Text" Value="{Binding AdditionalText}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="-----" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
То есть я хочу добится такого поведения, что при изменении переменных внутри любого класса (Server.IsEnabled или Profile.IsOnline к примеру) внутри соответствующих элементов изменялся текст
Server.IsEnabled --> ActionButton.Condition --> Изменения в xaml
Но так как мы передаем не ссылки на переменные, то когда эти переменные изменяется в классе, изменения не изменяются в ActionButton.
Передать bool из класса используя ref нельзя. (Property 'x' access returns temporary value. 'ref' argument must be an assignable variable, field, or an array element)
Единственное к чему я пришел это использование интерфейса, который содержит bool переменную
Измененный код:
public partial class DropdownMenuViewModel : ObservableObject
{
public Server Server;
public Profile Profile;
public ObservableCollection<ActionButton> Buttons { get; set; }
public DropdownMenuViewModel(Server server, Profile profile)
{
Server = server;
Profile = profile;
Buttons = new ObservableCollection<ActionButton>
{
new ActionButton("Img", "Включить сервер", Server, "Выключить"),
new ActionButton("Img", "Войти", Profile, "Выйти"),
};
}
}
public partial class Server : ObservableObject, IConditionChanged
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Condition))]
private bool _isEnabled;
public bool Condition => IsEnabled;
}
public partial class Profile : ObservableObject, IConditionChanged
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Condition))]
private bool _isOnline;
public bool Condition => IsOnline;
}
public class ActionButton(string imageUrl, string text, IConditionChanged? condition = null, string? additionalText = null)
{
public string ImageUrl { get; set; } = imageUrl;
public string Text { get; set; } = text;
public IConditionChanged? Condition { get; set; } = condition;
public string? AdditionalText { get; set; } = additionalText;
public bool IsHasCondition => Condition != null;
}
public interface IConditionChanged
{
bool Condition { get; }
}
И код XAML:
<Grid>
<ItemsControl Width="150" ItemsSource="{Binding Buttons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Background="Aqua">
<TextBlock Text="{Binding ImageUrl}" />
<TextBlock HorizontalAlignment="Left">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHasCondition}" Value="False">
<Setter Property="Text" Value="{Binding Text}" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsHasCondition}" Value="True" />
<Condition Binding="{Binding Condition.Condition}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Text" Value="{Binding Text}" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsHasCondition}" Value="True" />
<Condition Binding="{Binding Condition.Condition}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Text" Value="{Binding AdditionalText}" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="-----" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Как реализовать этот функционал правильным образом?
Является ли использование в классах которые являются представлением обьекта(model) использование INotifyPropertyChanged обобщенной практикой?