Код: Выделить всё
ProjectViewModelКак получить один экземпляр представления для каждой модели представления?
Каждый проект будет сильно настраивать представление, и нет смысла уничтожать/перестраивать представление каждый раз, когда я переключаюсь с проекта на другой.
Я заметил, что без ItemContainerStyle заголовок и тело табитема будут установлены с представлением, заголовок получит свой уникальный экземпляр, а тело - нет, т.е. используется одно и то же представление, что проблематично.
Итак, точнее: почему я получаю только один экземпляр представления для моделей представления одного типа в теле и один экземпляр представления для каждой модели представления в заголовке? И как получить один экземпляр представления для каждой модели представления в теле?
Repro: в следующей реализации каждый экземпляр представления будет иметь свой собственный уникальный идентификатор, чтобы проверить, имеет ли каждое представление собственный экземпляр или нет. Чтобы проверить это визуально, создайте как минимум 2 проекта одного и того же типа и убедитесь, что UniqueID представления один и тот же.
Здесь у меня есть модели VarioViewModels с разными конфигурациями (W, H), и поскольку представление относится к одному и тому же экземпляру, у меня нет правильной конфигурации сетки.
Суть проблемы:
Код: Выделить всё

Когда я выбираю элемент табуляции, щелкнув заголовок, я получаю правильное изменение модели представления, но подтверждается, что экземпляр представления одинаков для всех экземпляров модели представления одного и того же типа.
Код полного окна: MainWindow.xaml:
Код: Выделить всё
new tri
new vario
Код: Выделить всё
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace MultiTab;
public interface IProjectViewModel : ICloneable
{
public string ProjectTitle { get; set; }
public float Margin { get; set; }
public int W { get; }
public int H { get; }
}
public class Project : IProjectViewModel
{
public Project(string name) { ProjectTitle = name; }
public float Margin { get; set; }
public string ProjectTitle { get; set; }
public int W { get; protected set; }
public int H { get; protected set; }
public object Clone() => MemberwiseClone();
public override string ToString() => $"{ProjectTitle} {W}x{H}";
}
public class ProjectViewModel : Project
{
public ProjectViewModel(string name) : base(name)
{
W = 2;
H = 2;
}
}
public class ProjectVarioViewModel : Project
{
private Random _r = new();
public ProjectVarioViewModel(string name) : base(name)
{
W = _r.Next(2, 16);
H = _r.Next(2, 16);
}
}
public enum ProjectType
{
Triptych,
Vario,
}
public class MainViewModel : INotifyPropertyChanged
{
private IProjectViewModel? _selectedProject;
private DelegateCommandListen? _newProjectCommand;
private ICommand? _closeProjectCommand;
private Dictionary _argToProjectType = new()
{
{"Triptych", ProjectType.Triptych},
{"Vario", ProjectType.Vario},
};
public ObservableCollection Projects { get; } = new();
public IProjectViewModel? SelectedProject
{
get => _selectedProject;
set => SetField(ref _selectedProject, value);
}
public ICommand NewProjectCommand
{
get
{
return _newProjectCommand ?? (_newProjectCommand = new DelegateCommandListen(
s =>
{
var ptype = _argToProjectType[(string) s];
var name = $"{GetNewProjectTitle()} [{ptype.ToString()}]";
CreateNewProject(name, ptype);
},
s => true));
}
}
public ICommand CloseProjectCommand => _closeProjectCommand ?? (_closeProjectCommand = new DelegateCommandListen(
s =>
{
var closingvm = (IProjectViewModel)s;
Projects.Remove(closingvm);
},
s => s is not null));
private string GetNewProjectTitle() => $"new{Projects.Count}";
public void CreateNewProject(string projectname, ProjectType ptype)
{
IProjectViewModel vm;
switch (ptype)
{
case ProjectType.Triptych:
vm = new ProjectViewModel(projectname);
break;
default:
case ProjectType.Vario:
vm = new ProjectVarioViewModel(projectname);
break;
}
Projects.Add(vm);
SelectedProject = vm;
}
#region inpc
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion
}
PView:
Код: Выделить всё
Код: Выделить всё
using System.Windows.Controls;
using System.Xml;
namespace MultiTab
{
///
/// Interaction logic for TView.xaml
///
public partial class TView : UserControl
{
public TView()
{
InitializeComponent();
}
public UniqueId NameID { get; } = new();
}
}
Код: Выделить всё
x
Код: Выделить всё
public partial class VarioView : UserControl
{
private static Random _r = new ();
private SolidColorBrush GetRnColor()
{
var rgb = new byte[3];
_r.NextBytes(rgb);
return new SolidColorBrush(Color.FromRgb(rgb[0], rgb[1], rgb[2]));
}
public ProjectVarioViewModel ViewModel
{
get => (ProjectVarioViewModel)DataContext;
set => DataContext = value;
}
public UniqueId NameID { get; } = new();
public VarioView()
{
InitializeComponent();
Loaded += ApplyGrid;
}
private void ApplyGrid(object sender, RoutedEventArgs e)
{
var totCols = ViewModel.W;
var totRows = ViewModel.H;
var zoneWLen = new GridLength(1, GridUnitType.Star);
for (int x = 0; x < totCols; x++)
GridZonesContainer.ColumnDefinitions.Add(new ColumnDefinition { Width = zoneWLen });
for (int y = 0; y < totRows; y++)
GridZonesContainer.RowDefinitions.Add(new RowDefinition { Height = zoneWLen });
for (int x = 0; x < totCols; x++)
for (int y = 0; y < totRows; y++)
AddZone(x, y);
}
private void AddZone(int gridx, int gridy)
{
var mz = new Rectangle
{
Fill = GetRnColor()
};
GridZonesContainer.Children.Add(mz);
Grid.SetRow(mz, gridy);
Grid.SetColumn(mz, gridx);
}
}
Код: Выделить всё
using System.ComponentModel;
using System.Linq.Expressions;
using System.Windows.Input;
namespace MultiTab;
///
/// Implementation of ICommand with listening to 1+ properties change (INPC)
/// ICommand Zcommand = new DelegateCommandListen {
/// ExecuteDelegate = ZExecuteCommand,
/// CanExecuteDelegate = CanExecuteZCommand }.ListenOn(this, o => o.INPCpropFromVM);;
///
public class DelegateCommandListen : ICommand
{
private readonly List _controlEvent;
private Action _executeDelegate;
///
/// Implementation of ICommand with listening to 1+ properties change (INPC)
/// ICommand Zcommand = new DelegateCommandListen {
/// ExecuteDelegate = ZExecuteCommand,
/// CanExecuteDelegate = CanExecuteZCommand }.ListenOn(this, o => o.INPCpropFromVM);;
///
public DelegateCommandListen()
{
_controlEvent = new List();
}
///
/// Implementation of ICommand with listening to 1+ properties change (INPC)
///
///
///
public DelegateCommandListen(Action executeDelegate, Predicate canExecuteDelegate)
{
_controlEvent = new List();
ExecuteDelegate = executeDelegate;
CanExecuteDelegate = canExecuteDelegate;
}
public List PropertiesToListenTo { get; set; }
public Predicate CanExecuteDelegate { get; set; }
public Action ExecuteDelegate
{
get { return _executeDelegate; }
set
{
_executeDelegate = value;
ListenForNotificationFrom((INotifyPropertyChanged)_executeDelegate.Target);
}
}
public void RaiseCanExecuteChanged()
{
if (_controlEvent is { Count: > 0 })
_controlEvent.ForEach(ce => { ((EventHandler)ce.Target)?.Invoke(null, EventArgs.Empty); });
}
public DelegateCommandListen ListenOn
(TObservedType viewModel, Expression propertyExpression)
where TObservedType : INotifyPropertyChanged
{
var propertyName = GetPropertyName(propertyExpression);
viewModel.PropertyChanged += (s, e) =>
{
if (e.PropertyName == propertyName) RaiseCanExecuteChanged();
};
return this;
}
public void ListenForNotificationFrom(TObservedType viewModel)
where TObservedType : INotifyPropertyChanged
{
viewModel.PropertyChanged += (s, e) => RaiseCanExecuteChanged();
}
private static string GetPropertyName(Expression expression)
where T : INotifyPropertyChanged
{
var lambda = expression as LambdaExpression;
var memberInfo = GetMemberExpression(lambda).Member;
return memberInfo.Name;
}
private static MemberExpression GetMemberExpression(LambdaExpression lambda)
{
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression body)
{
var unaryExpression = body;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
memberExpression = lambda.Body as MemberExpression;
return memberExpression;
}
#region ICommand Members
public bool CanExecute(object parameter) => CanExecuteDelegate == null || CanExecuteDelegate(parameter);
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
_controlEvent.Add(new WeakReference(value));
}
remove
{
CommandManager.RequerySuggested -= value;
_controlEvent.Remove(_controlEvent.Find(r => (EventHandler)r.Target == value));
}
}
public void Execute(object parameter) => ExecuteDelegate?.Invoke(parameter);
#endregion
}
Подробнее здесь: https://stackoverflow.com/questions/797 ... tabcontrol
Мобильная версия