Тут маленький мануал который описан очень сжато и читать его нужно просматривая сам проект.
Ссылка на него.
Для того чтобы сделать свой customcontrol нужно две вещи:
1. View представление самого элемента.
2. Класс который будем контекстом данных для этого View представления.
Наше приложение будет базироваться на шаблоне MVVM, если незнаком с этим читаем
тут. Так же необходимо понимать что такое binding в WPF,если незнаем читаем
тут и
тут. Если кратко то связывание, это такая штука
которая позволяет обновлять состояние UI элемента при изменении его свойств в контексте данных, если этого не сделать то тогда обновлять состояние в UI нужно будет через Dispacher а это будет фризить
наш UI и так делать не нада. Поэтому добавим в наш проект Папку Services и туда добавим классы ObservableObject и RelayCommand (Код все[ классов находиться в репозитории). В ObservableObject будет описан механизм связывания свойств и мы будет просто наследоваться от этого класса, вызывая метод OnPropertyChanged в set параметре свойств.
Связывание наших свойств разделим на два этапа, сначала сделаем события обновления данных в самой модели кнопки а затем укажем к какому контексту данных привязаться и с какими свойствами связать в XAML (сама наша кнопка).
Сначала опишем интерфейс кнопки типом IButtonStyle и создадим абстрактный класс AButton который наследует ObservableObject,IButtonStyle. В классе AButton добавим поля отражающие свойства в IButtonStyle.
private string _content;
public string content
{
get => _content;
set { OnPropertyChanged(ref _content, value); }
}
Тут реализовано свойство наследованное из интерфейса IButtonStyle (content) которое возвр. поле _content и устанавливает значение с помощью метода OnPropertyChanged(ref _content, value), и тоже самое проделаем со всеми остальными свойствами которые мы наследуем. Потом мы свяжем эти свойства со свойствами нашего UserControl в XAML.
<Label Content="{Binding content}" Foreground="{Binding fontColor}" FontSize="{Binding fontSize}"/>
Теперь нам нужно сделать так чтобы при наведении курсора на наш элемент он изменял цвет. Для этого нам нужно создать свойства зависимости типа RelayCommand для нашей кнопки
private RelayCommand _mouseEnterCommand;
/// <summary>
/// Команда входа мышки в поле контролера
/// </summary>
public RelayCommand MouseEnterCommand
{
get { return _mouseEnterCommand; }
set
{
OnPropertyChanged(ref _mouseEnterCommand, value);
}
}
private RelayCommand _mouseLeaveCommand;
/// <summary>
/// Команда покидания мышки поля контролера
/// </summary>
public RelayCommand MouseLeaveCommand
{
get { return _mouseLeaveCommand; }
}
private RelayCommand _mouseDownCommand;
/// <summary>
/// Команда щелчка мышки
/// </summary>
public RelayCommand MouseDownCommand
{
get { return _mouseDownCommand; }
}
И сделаем пару событий и методов для непосредственной установки значений (цвет или размер шрифта)
event Action _mouseEnter;
event Action _mouseLeave;
void MouseEnterEventMethod()
{
fontColor = fontColorMouseEnter;
}
void MouseLeaveEventMethod()
{
fontColor = fontColorNormal;
}
private void MouseEnter()
{
this._mouseEnter.Invoke();
}
private void MouseLeave()
{
this._mouseLeave.Invoke();
}
public void MouseEnter(object param)
{
MouseEnter();
}
public void MouseLeave(object param)
{
MouseLeave();
}
И подпишемся на это в конструкторе класса
public AButton(string _content)
{
content = _content;
_mouseEnter += this.MouseEnterEventMethod;
_mouseLeave += this.MouseLeaveEventMethod;
this._mouseEnterCommand = new RelayCommand(MouseEnter);
this._mouseLeaveCommand = new RelayCommand(MouseLeave);
}
И так у нас теперь есть свойства content, fontColor, fontColorNormal, fontColorMouseEnter и комманды MouseEnterCommand, MouseLeaveCommand, MouseDownCommand готовые для сзязывания.
Ну и сделаем пару классов наших кнопок
class ButtonStyleA : AButton
{
public ButtonStyleA(string _content) : base()
{
content = _content;
base.fontColor = "#FF5733";
base.fontColorNormal = "#FF5733";
base.fontColorMouseEnter = "#61FF33";
fontSize = 16;
}
}
class ButtonStyleB : AButton
{
public ButtonStyleB(string _content) : base()
{
content = _content;
fontColor = "#33B5FF";
fontColorNormal = "#33B5FF";
fontColorMouseEnter = "#E933FF";
fontSize = 12;
}
}
Теперь нам нужно создать контекст данных для нашего элемента, поэтому в папке ViewModels Создадим класс
public class MenuButtonViewModel
{
public AButton MenuButton { get; set; }
public MenuButtonViewModel(AButton menuButton)
{
MenuButton = menuButton;
}
}
Очень часто все связывания и Комманды реализовывают иммено в классе контекста данных (MenuButtonViewModel).
Создадим наш UserControl в папке Views назовем его MyButton и добавим ссылку на класс MouseBehaviour.
xmlns:u="clr-namespace:WpfMvvmDemo.Services"
Теперь свяжем события MouseMoveCommand, MouseLeaveCommand, MouseDownCommand с нашими свойствами типа RelayCommand.
<Grid u:MouseBehaviour.MouseMoveCommand="{Binding MenuButton.MouseEnterCommand}" u:MouseBehaviour.MouseLeaveCommand="{Binding MenuButton.MouseLeaveCommand}" u:MouseBehaviour.MouseDownCommand="{Binding MenuButton.MouseDownCommand}">
И также свяжем свойсва Label со свойствами AButton
<Label Content="{Binding MenuButton.content}" Foreground="{Binding MenuButton.fontColor}" FontSize="{Binding MenuButton.fontSize}"/>
Теперь нам нужен контекст данных для главного окна нашего приложения. Назовем его RootViewModel и создадим в нем две кнопки разных типов ButtonStyleA и ButtonStyleВ.
public class RootViewModel : ObservableObject
{
public MenuButtonViewModel MainPageButton { get; set; }
public MenuButtonViewModel SecondPageButton { get; set; }
public RootViewModel()
{
MainPageButton = new MenuButtonViewModel(new ButtonStyleA("Главная"));
SecondPageButton = new MenuButtonViewModel(new ButtonStyleB("Вторая"));
}
private void ChangeToMainPage()
{
}
private void ChangeToSecondPage()
{
}
}
В Классе MainWindow создадим экземпляр типа RootViewModel и сделаем его контекстом данных для MainWindow
private RootViewModel RootVM;
public MainWindow()
{
InitializeComponent();
RootVM = new RootViewModel();
this.DataContext = RootVM;
}
В XAML представлении нашего главного окна MainWindow добавим ссылку на наш CustomController
xmlns:bn="clr-namespace:WpfMvvmDemo.Views"
и теперь пересоберем проект а то ide может не увидеть наши кнопки, и потом добавим сообственно сами кнопки и установим для них контекст данных (это те свойства типа MenuButtonViewModel которые мы создали в RootViewModel)
<Grid >
<StackPanel Orientation="Vertical">
<bn:MyButton DataContext="{Binding MainPageButton}" />
<bn:MyButton DataContext="{Binding SecondPageButton}"/>
</StackPanel>
</Grid>
Ну вот собственно и все, можно запускать и радоваться. За г0внокод просьба не ругать.