Как иметь один экземпляр представления для каждой модели представления в TabControl?C#

Место общения программистов C#
Ответить
Anonymous
 Как иметь один экземпляр представления для каждой модели представления в TabControl?

Сообщение Anonymous »

У меня есть TabControl, показывающий представления из проектов ObservableCollection.

Код: Выделить всё

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

Когда я выбираю элемент табуляции, щелкнув заголовок, я получаю правильное изменение модели представления, но подтверждается, что экземпляр представления одинаков для всех экземпляров модели представления одного и того же типа.
Код полного окна: 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 и VarioView.
PView: PПросмотр кода:

Код: Выделить всё

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();
}
}
VarioView: Код VarioView:

Код: Выделить всё

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);
}
}
Утилита DelegateCommandListen:

Код: Выделить всё

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
}
Репозиторий Git: MultiTab


Подробнее здесь: https://stackoverflow.com/questions/797 ... tabcontrol
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»