Коммит
d56466f9dd
|
@ -101,6 +101,10 @@ Each sample is tagged with it's difficulty. The degree of difficulty describes h
|
|||
| 🐔 Normal
|
||||
| Game, Canvas, Game Loop, MVVM
|
||||
|
||||
| link:src/Avalonia.Samples/Drawing/RectPainter[Rect Painter Sample]
|
||||
| 🐔 Normal
|
||||
| Graphics, MVVM
|
||||
|
||||
|===
|
||||
|
||||
=== 🎞️ DataTemplate-Samples
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.2.32616.157
|
||||
|
@ -40,20 +39,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Folder", "Solution
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvvmDialogSample", "ViewInteraction\MvvmDialogSample\MvvmDialogSample.csproj", "{48432457-6A55-4D03-9D40-260CE8E06440}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DialogManagerSample", "ViewInteraction\DialogManagerSample\DialogManagerSample.csproj", "{0BC90E92-D8B3-4C6D-8C47-BAF57CD73CBA}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DialogManagerSample", "ViewInteraction\DialogManagerSample\DialogManagerSample.csproj", "{0BC90E92-D8B3-4C6D-8C47-BAF57CD73CBA}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{85B157B3-F701-4F75-B4F1-EC2287729480}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableApp", "Testing\TestableApp\TestableApp.csproj", "{326EF526-6200-4570-90DE-5E6D48B63EAC}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestableApp", "Testing\TestableApp\TestableApp.csproj", "{326EF526-6200-4570-90DE-5E6D48B63EAC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableApp.Headless.NUnit", "Testing\TestableApp.Headless.NUnit\TestableApp.Headless.NUnit.csproj", "{B8CB5C57-07ED-4BC6-ACE8-F05E428E3EB5}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestableApp.Headless.NUnit", "Testing\TestableApp.Headless.NUnit\TestableApp.Headless.NUnit.csproj", "{B8CB5C57-07ED-4BC6-ACE8-F05E428E3EB5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableApp.Headless.XUnit", "Testing\TestableApp.Headless.XUnit\TestableApp.Headless.XUnit.csproj", "{BDA7536E-26FD-436F-AAC8-F8A2B500548E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestableApp.Headless.XUnit", "Testing\TestableApp.Headless.XUnit\TestableApp.Headless.XUnit.csproj", "{BDA7536E-26FD-436F-AAC8-F8A2B500548E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableApp.Appium", "Testing\TestableApp.Appium\TestableApp.Appium.csproj", "{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestableApp.Appium", "Testing\TestableApp.Appium\TestableApp.Appium.csproj", "{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ViewInteraction", "ViewInteraction", "{2E99F15F-A82A-4734-A837-C0F768702600}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RectPainter", "Drawing\RectPainter\RectPainter.csproj", "{2B746401-384F-484A-810E-7A65288165E0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -124,6 +125,10 @@ Global
|
|||
{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2B746401-384F-484A-810E-7A65288165E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2B746401-384F-484A-810E-7A65288165E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2B746401-384F-484A-810E-7A65288165E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B746401-384F-484A-810E-7A65288165E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -139,12 +144,13 @@ Global
|
|||
{53239038-E234-4DEF-B730-953B2F43B51E} = {932FD4A5-FCE7-4428-A0C1-C0392D90A21A}
|
||||
{CC74DC68-A0A5-40A2-9A4D-CF110685D8C4} = {92C71AA7-E791-40C0-9F3A-2A85880B0439}
|
||||
{17F36A0B-940E-49DB-B5B5-38C38E0947EE} = {D02161B3-8242-4BF5-96E9-780465A5023B}
|
||||
{48432457-6A55-4D03-9D40-260CE8E06440} = {2E99F15F-A82A-4734-A837-C0F768702600}
|
||||
{0BC90E92-D8B3-4C6D-8C47-BAF57CD73CBA} = {2E99F15F-A82A-4734-A837-C0F768702600}
|
||||
{326EF526-6200-4570-90DE-5E6D48B63EAC} = {85B157B3-F701-4F75-B4F1-EC2287729480}
|
||||
{B8CB5C57-07ED-4BC6-ACE8-F05E428E3EB5} = {85B157B3-F701-4F75-B4F1-EC2287729480}
|
||||
{BDA7536E-26FD-436F-AAC8-F8A2B500548E} = {85B157B3-F701-4F75-B4F1-EC2287729480}
|
||||
{F5CB3DA2-EB59-4792-A1B3-49F600F7C130} = {85B157B3-F701-4F75-B4F1-EC2287729480}
|
||||
{48432457-6A55-4D03-9D40-260CE8E06440} = {2E99F15F-A82A-4734-A837-C0F768702600}
|
||||
{0BC90E92-D8B3-4C6D-8C47-BAF57CD73CBA} = {2E99F15F-A82A-4734-A837-C0F768702600}
|
||||
{2B746401-384F-484A-810E-7A65288165E0} = {D02161B3-8242-4BF5-96E9-780465A5023B}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C246CAB0-0837-4EE4-A22D-28B3C74930B4}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="RectPainter.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
</Application.Styles>
|
||||
</Application>
|
|
@ -0,0 +1,31 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using RectPainter.ViewModels;
|
||||
|
||||
namespace RectPainter;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var mainWindow = new MainWindow();
|
||||
|
||||
var vm = new MainWindowViewModel()
|
||||
{
|
||||
Vm = new PaintControlViewModel()
|
||||
};
|
||||
mainWindow.DataContext = vm;
|
||||
desktop.MainWindow = mainWindow;
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 172 KiB |
|
@ -0,0 +1,203 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using RectPainter.ViewModels;
|
||||
|
||||
namespace RectPainter.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A control that responds to mouse and keyboard, to edit and render an image
|
||||
/// </summary>
|
||||
public class PaintControl : Control
|
||||
{
|
||||
public PaintControlViewModel? Vm => DataContext as PaintControlViewModel;
|
||||
|
||||
public PaintControl()
|
||||
{
|
||||
// Setup event handlers
|
||||
PointerMoved += PaintControl_PointerMoved;
|
||||
PointerPressed += PaintControl_PointerPressed;
|
||||
PointerReleased += PaintControl_PointerReleased;
|
||||
PointerCaptureLost += PaintControl_PointerCaptureLost;
|
||||
SizeChanged += PaintControl_SizeChanged;
|
||||
|
||||
KeyDownEvent.AddClassHandler<TopLevel>(PaintControl_KeyDown, handledEventsToo: true);
|
||||
KeyUpEvent.AddClassHandler<TopLevel>(PaintControl_KeyUp, handledEventsToo: true);
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (change.Property == DataContextProperty && Bounds.Size != default)
|
||||
{
|
||||
// let the view model know the PaintControl's size, so the image can be the correct size.
|
||||
Vm?.SetImageSize(new PixelSize((int)Bounds.Width, (int)Bounds.Height));
|
||||
|
||||
// Request the image be rendered
|
||||
InvalidateVisual();
|
||||
}
|
||||
}
|
||||
|
||||
private void PaintControl_PointerMoved(object? sender, Avalonia.Input.PointerEventArgs e)
|
||||
{
|
||||
if (Vm != null)
|
||||
{
|
||||
// Update the mouse position, and the currently selected marquee
|
||||
var pos = e.GetPosition(this);
|
||||
Vm.Pos = pos;
|
||||
if (Vm.Dragging)
|
||||
{
|
||||
Vm.Marquee = new Rect(
|
||||
System.Math.Min(Vm.Origin.X, Vm.Pos.X),
|
||||
System.Math.Min(Vm.Origin.Y, Vm.Pos.Y),
|
||||
System.Math.Abs(Vm.Origin.X - Vm.Pos.X),
|
||||
System.Math.Abs(Vm.Origin.Y - Vm.Pos.Y));
|
||||
InvalidateVisual();
|
||||
}
|
||||
else
|
||||
{
|
||||
Vm.Origin = pos;
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void PaintControl_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
|
||||
{
|
||||
if (Vm != null)
|
||||
{
|
||||
// Start the drag
|
||||
Vm.Dragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void PaintControl_PointerReleased(object? sender, Avalonia.Input.PointerReleasedEventArgs e)
|
||||
{
|
||||
if (Vm != null)
|
||||
{
|
||||
if (Vm.Dragging == true)
|
||||
{
|
||||
// Finish dragging
|
||||
Vm.Dragging = false;
|
||||
|
||||
// Paint a new rectangle
|
||||
Vm.AddRectangle();
|
||||
|
||||
// Request the updated image be rendered
|
||||
InvalidateVisual();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PaintControl_PointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
|
||||
{
|
||||
if (Vm != null)
|
||||
{
|
||||
// finish Dragging
|
||||
Vm.Dragging = false;
|
||||
|
||||
// Request the image be rendered (to clear any marquee)
|
||||
InvalidateVisual();
|
||||
}
|
||||
}
|
||||
|
||||
private void PaintControl_SizeChanged(object? sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (Vm != null)
|
||||
{
|
||||
// Make sure the image matches the size of the control
|
||||
Vm.SetImageSize(new PixelSize((int)e.NewSize.Width, (int)e.NewSize.Height));
|
||||
|
||||
// Request the updated image be rendered
|
||||
InvalidateVisual();
|
||||
}
|
||||
}
|
||||
|
||||
private void PaintControl_KeyDown(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (Vm != null)
|
||||
{
|
||||
// Change rectangle color or cancel dragging
|
||||
// Request the updated image be rendered, in case there is a marquee
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.LeftShift:
|
||||
Vm.Red = 255;
|
||||
InvalidateVisual();
|
||||
break;
|
||||
|
||||
case Key.LeftCtrl:
|
||||
Vm.Green = 255;
|
||||
InvalidateVisual();
|
||||
break;
|
||||
|
||||
case Key.LeftAlt:
|
||||
Vm.Blue = 255;
|
||||
InvalidateVisual();
|
||||
break;
|
||||
|
||||
case Key.Escape:
|
||||
Vm.Dragging = false;
|
||||
InvalidateVisual();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PaintControl_KeyUp(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (Vm != null)
|
||||
{
|
||||
// Change rectangle color
|
||||
// Request the updated image be rendered, in case there is a marquee
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.LeftShift:
|
||||
Vm.Red = 0;
|
||||
InvalidateVisual();
|
||||
break;
|
||||
|
||||
case Key.LeftCtrl:
|
||||
Vm.Green = 0;
|
||||
InvalidateVisual();
|
||||
break;
|
||||
|
||||
case Key.LeftAlt:
|
||||
Vm.Blue = 0;
|
||||
InvalidateVisual();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Render the saved graphic, and marquee when needed
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
// If there is an image in the view model, copy it to the PaintControl's drawing surface
|
||||
if (Vm?.Image != null)
|
||||
{
|
||||
context.DrawImage(Vm.Image, Bounds);
|
||||
}
|
||||
|
||||
// If we are in a dragging operation, draw a dashed rectangle
|
||||
// The base color for the rectangle is the color for rectangle that the drag operation will draw
|
||||
// The alternative color is either black or white to contrast with the base color
|
||||
if (Vm?.Dragging == true) {
|
||||
var pen = new Pen(new SolidColorBrush(Color.FromRgb(Vm.Red, Vm.Green, Vm.Blue)));
|
||||
context.DrawRectangle(pen, Vm.Marquee.Translate(new Vector(0.5, 0.5)));
|
||||
byte altColor = (byte)(255 - Vm.Green);
|
||||
pen = new Pen(new SolidColorBrush(Color.FromRgb(altColor, altColor, altColor)), dashStyle: DashStyle.Dash);
|
||||
context.DrawRectangle(pen, Vm.Marquee.Translate(new Vector(0.5, 0.5)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="using:RectPainter.Controls"
|
||||
xmlns:vm="using:RectPainter.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="RectPainter.MainWindow"
|
||||
Title="Rect Painter Sample - Drag out rectangles with the mouse. Use Shift/Ctrl/Alt to change color"
|
||||
Icon="/Assets/avalonia-logo.ico">
|
||||
<Design.DataContext>
|
||||
<vm:MainWindowViewModel/>
|
||||
</Design.DataContext>
|
||||
|
||||
<DockPanel>
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom">
|
||||
<TextBlock Width="50" Text="Cursor:"/>
|
||||
<TextBlock Width="120" Text="{Binding MousePosition}"/>
|
||||
<TextBlock Width="70" Text="Rectangle:"/>
|
||||
<TextBlock Width="100" Text="{Binding Rect}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="Use mouse with Shift/Alt/Ctrl key modifiers pressed."/>
|
||||
<controls:PaintControl DataContext="{Binding Vm}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
||||
</DockPanel>
|
||||
</Window>
|
|
@ -0,0 +1,12 @@
|
|||
using Avalonia.Controls;
|
||||
|
||||
namespace RectPainter
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using Avalonia;
|
||||
|
||||
namespace RectPainter;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
{
|
||||
return AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
# Rect Painter Sample
|
||||
|
||||
<!-- Write a short summary here what this examples does -->
|
||||
This example will show you how to create a custom rendered control which interacts with the mouse, to form a simple paint application
|
||||
|
||||
|
||||
### Difficulty
|
||||
<!-- Choose one of the below difficulties. You can just delete the ones you don't need. -->
|
||||
|
||||
🐔 Normal 🐔
|
||||
|
||||
|
||||
|
||||
### Buzz-Words
|
||||
|
||||
<!-- Write some buzz-words here. You can separate them by ", " -->
|
||||
Graphics Editor, Paint, MVVM
|
||||
|
||||
|
||||
|
||||
## The Solution
|
||||
|
||||
There is a custom control `PaintControl` and a corresponding view model for that control `PaintControlViewModel`.
|
||||
|
||||
The view model holds data for the control as properties, and also holds the off-screen image which is being edited.
|
||||
|
||||
The `PaintControl` :-
|
||||
|
||||
* responds to mouse movement, mouse button presses, and also to global keyboard events
|
||||
* maintains information about the editing process, what rectangle you are dragging out, and what color it is, in the view model.
|
||||
* requests the view model to modify the image when you create a new rectangle, or when the image size needs to change in response to the application resizing.
|
||||
|
||||
The `PaintControlViewModel` :-
|
||||
|
||||
* publishes the properties it holds using the `INotifyPropertyChanged` interface.
|
||||
* handles the actual editing of the image.
|
||||
|
||||
The `MainWindowViewModel` :-
|
||||
|
||||
* subscribes to properties in the `PaintControlViewModel` so that it can provide properties to the UI for binding
|
||||
|
||||
## Notes
|
||||
<!-- Any related information or further readings goes here. -->
|
||||
|
||||
The `PaintControlViewModel` holds a single image that is being edited, and the `PaintControl` renders that image when needed. Editing the image involves creating a new image, painting the old one onto it, and then adding the newly requested rectangle.
|
||||
|
||||
More control and editing capabilities could be acheived using a dedicated graphics library such as Skia.
|
|
@ -0,0 +1,23 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<OutputType>WinExe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.0.0" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.0" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.0" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,106 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace RectPainter.ViewModels
|
||||
{
|
||||
public class MainWindowViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private string _mousePosition = string.Empty;
|
||||
private string _rect = string.Empty;
|
||||
private PaintControlViewModel? _vm = null;
|
||||
|
||||
// A text rendering of the current mouse position
|
||||
public string MousePosition
|
||||
{
|
||||
get => _mousePosition;
|
||||
set
|
||||
{
|
||||
_mousePosition = value;
|
||||
OnPropertyChanged(nameof(MousePosition));
|
||||
}
|
||||
}
|
||||
|
||||
// A text rendering of the current marquee dimensions
|
||||
public string Rect
|
||||
{
|
||||
get => _rect;
|
||||
set
|
||||
{
|
||||
_rect = value;
|
||||
OnPropertyChanged(nameof(Rect));
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
protected void OnPropertyChanged(string name)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
}
|
||||
|
||||
// The view model for the PaintControl, is a child of this view model
|
||||
// just as the PaintControl itself is a child of the MainWindow
|
||||
public PaintControlViewModel? Vm {
|
||||
get => _vm;
|
||||
set
|
||||
{
|
||||
if (_vm != value)
|
||||
{
|
||||
// Remove event handlers from any previous view model
|
||||
if (_vm != null)
|
||||
_vm.PropertyChanged -= _vm_PropertyChanged;
|
||||
|
||||
_vm = value;
|
||||
|
||||
// Add event handlers for this view model
|
||||
if (_vm != null)
|
||||
_vm.PropertyChanged += _vm_PropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _vm_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(PaintControlViewModel.Dragging):
|
||||
{
|
||||
// Update the text for Position and Marquee
|
||||
SetPos();
|
||||
SetRect();
|
||||
break;
|
||||
}
|
||||
|
||||
case nameof(PaintControlViewModel.Pos):
|
||||
{
|
||||
// Update the text for Position
|
||||
SetPos();
|
||||
break;
|
||||
}
|
||||
|
||||
case nameof(PaintControlViewModel.Marquee):
|
||||
{
|
||||
// Update the text for the Marquee
|
||||
SetRect();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPos()
|
||||
{
|
||||
MousePosition = $"{Vm?.Pos.X} {Vm?.Pos.Y}";
|
||||
}
|
||||
|
||||
private void SetRect()
|
||||
{
|
||||
if (Vm?.Dragging == true)
|
||||
{
|
||||
Rect = $"{Vm.Marquee.Left} {Vm.Marquee.Top} {Vm.Marquee.Width} {Vm.Marquee.Height}";
|
||||
}
|
||||
else
|
||||
{
|
||||
Rect = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace RectPainter.ViewModels
|
||||
{
|
||||
public class PaintControlViewModel: INotifyPropertyChanged
|
||||
{
|
||||
private bool _dragging = false;
|
||||
private Point _origin = new Point(0, 0);
|
||||
private Point _pos = new Point(0, 0);
|
||||
private Rect _marquee = new Rect();
|
||||
private RenderTargetBitmap? _image = null;
|
||||
|
||||
// Are we in a drag operation?
|
||||
public bool Dragging
|
||||
{
|
||||
get => _dragging;
|
||||
set
|
||||
{
|
||||
if (_dragging != value)
|
||||
{
|
||||
_dragging = value;
|
||||
OnPropertyChanged(nameof(Dragging));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The current mouse position
|
||||
public Point Pos
|
||||
{
|
||||
get => _pos;
|
||||
set
|
||||
{
|
||||
if (_pos != value)
|
||||
{
|
||||
_pos = value;
|
||||
OnPropertyChanged(nameof(Pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The mouse position at the start of the drag operation
|
||||
public Point Origin
|
||||
{
|
||||
get => _origin;
|
||||
set
|
||||
{
|
||||
if (_origin != value)
|
||||
{
|
||||
_origin = value;
|
||||
OnPropertyChanged(nameof(Origin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The drag rectangle
|
||||
public Rect Marquee
|
||||
{
|
||||
get => _marquee;
|
||||
set
|
||||
{
|
||||
if (_marquee != value)
|
||||
{
|
||||
_marquee = value;
|
||||
OnPropertyChanged(nameof(Marquee));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The color for the rectangle to be drawn
|
||||
// This can be controlled by pressing Shift, Ctrl and Alt keys
|
||||
public byte Red { get; set; } = 0;
|
||||
public byte Green { get; set; } = 0;
|
||||
public byte Blue { get; set; } = 0;
|
||||
|
||||
// The bitmap full of rectangles
|
||||
public IImage? Image
|
||||
{
|
||||
get => _image;
|
||||
}
|
||||
|
||||
// When the control changes, we need to change the bitmap to match
|
||||
public void SetImageSize(PixelSize size)
|
||||
{
|
||||
RenderTargetBitmap newImage = new RenderTargetBitmap(new PixelSize(size.Width, size.Height), new Vector(96, 96));
|
||||
if (_image != null)
|
||||
{
|
||||
// If there was already a bitmap, copy it into the new one.
|
||||
using (var context = newImage.CreateDrawingContext())
|
||||
{
|
||||
context.DrawImage(_image, new Rect(0, 0, size.Width, size.Height), new Rect(0, 0, size.Width, size.Height));
|
||||
}
|
||||
}
|
||||
_image = newImage;
|
||||
OnPropertyChanged(nameof(Image));
|
||||
}
|
||||
|
||||
public void AddRectangle()
|
||||
{
|
||||
if (_image != null)
|
||||
{
|
||||
// Create a new image, copy the old one, and then add a new rectangle to it
|
||||
// using the current marquee and color
|
||||
RenderTargetBitmap newImage = new RenderTargetBitmap(_image.PixelSize, _image.Dpi);
|
||||
using (var context = newImage.CreateDrawingContext())
|
||||
{
|
||||
context.DrawImage(_image, new Rect(0, 0, _image.PixelSize.Width, _image.PixelSize.Height));
|
||||
var brush = new SolidColorBrush(Color.FromRgb(Red, Green, Blue));
|
||||
context.FillRectangle(brush, _marquee);
|
||||
}
|
||||
_image.Dispose();
|
||||
_image = newImage;
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
protected void OnPropertyChanged(string name)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
}
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 7.5 KiB |
Загрузка…
Ссылка в новой задаче