У меня проблема с моим пользовательским стековым макетом, который правильно заполняет стековый макет, но не распознает никаких изменений каких-либо элементов в связанной наблюдаемой коллекции.
Это код, который я использую для связываемого стекового макета:
public class BindableStackLayout : StackLayout
{
private readonly Label _header;
public BindableStackLayout()
{
_header = new Label();
Children.Add(_header);
}
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable),
typeof(BindableStackLayout), propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());
public DataTemplate ItemDataTemplate
{
get => (DataTemplate)GetValue(ItemDataTemplateProperty);
set => SetValue(ItemDataTemplateProperty, value);
}
public static readonly BindableProperty ItemDataTemplateProperty = BindableProperty.Create(nameof(ItemDataTemplate),
typeof(DataTemplate), typeof(BindableStackLayout));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string),
typeof(BindableStackLayout), propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateHeader());
private void PopulateItems()
{
if (ItemsSource == null)
return;
foreach (var item in ItemsSource)
{
var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
itemTemplate.BindingContext = item;
Children.Add(itemTemplate);
}
}
private void PopulateHeader() => _header.Text = Title;
}
Который используется, как вы можете найти здесь:
<ContentView.Content>
<h:BindableStackLayout ItemsSource="{Binding MenuHotKeys, Mode=TwoWay}"
Style="{StaticResource MenuControlStackLayout}">
<h:BindableStackLayout.ItemDataTemplate>
<DataTemplate>
<Button Text="{Binding DataA}"
Command="{Binding Path=BindingContext.MenuControlCommand, Source={x:Reference InternalMenuControl}}"
CommandParameter="{Binding .}"
Style="{StaticResource MenuControlButton}"/>
</DataTemplate>
</h:BindableStackLayout.ItemDataTemplate>
</h:BindableStackLayout>
</ContentView.Content>
И в ViewModel у меня есть этот код:
private ObservableCollection<ConfigMenuItem> _menuHotKeys;
public ObservableCollection<ConfigMenuItem> MenuHotKeys
{
get => _menuHotKeys;
set => SetValue(ref _menuHotKeys, value);
}
И изменение здесь:
private async void MenuControlButtonPressed(object sender)
{
var menuItem = sender as ConfigMenuItem;
if (menuItem.ItemId == _expanderId)
{
// toggle expanded menu visibility
var expander = _menuHotKeys.FirstOrDefault(p => p.ItemId == _expanderId);
var buffer = expander.DataA;
expander.DataA = expander.DataB;
expander.DataB = buffer;
}
else
{
await NavigationHandler.NavigateToMenuItem(menuItem);
}
}
Как видите, я хочу переключить имя связанной кнопки, но изменения не появляются.
Я думаю, что мне нужно что-то изменить в классе связываемых стековых макетов,но что?
Может быть, вы можете помочь
@ INPC ответы: ConfigMenuItem в Коллекции происходит из:
public abstract class BaseObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void SetValue<T>(ref T field, T value, Expression<Func<T>> property)
{
if (!ReferenceEquals(field, value))
{
field = value;
OnPropertyChanged(property);
}
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> changedProperty)
{
if (PropertyChanged != null)
{
string name = ((MemberExpression)changedProperty.Body).Member.Name;
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
, а модель представления происходит из:
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void SetValue<T>(ref T privateField, T value, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(privateField, value))
{
privateField = value;
OnPropertyChanged(propertyName);
}
return;
}
}
В соответствии с просьбой в комментариях класс ConfigMenuItem, код BaseObject см. В верхней части кода:
public class ConfigMenuItem : BaseObject, IConfigMenuItem
{
public int ItemId
{
get;
set;
}
public int Position
{
get;
set;
}
public string Name
{
get;
set;
}
public string DataA
{
get;
set;
}
public string DataB
{
get;
set;
}
public bool IsEnabled
{
get;
set;
}
public bool IsHotKey
{
get;
set;
}
public bool IsCustomMenuItem
{
get;
set;
}
public override string ToString()
{
return $"{Name} ({DataA} | {DataB ?? "null"})";
}
}