This commit is contained in:
2026-04-12 22:13:00 +03:00
parent 07680fc5ed
commit ed3291c727
26 changed files with 1128 additions and 391 deletions
+4
View File
@@ -4,8 +4,12 @@
<option name="projectPerEditor"> <option name="projectPerEditor">
<map> <map>
<entry key="MCCAD/App.axaml" value="MCCAD/MCCAD.csproj" /> <entry key="MCCAD/App.axaml" value="MCCAD/MCCAD.csproj" />
<entry key="MCCAD/Views/DrawingView.axaml" value="MCCAD/MCCAD.csproj" />
<entry key="MCCAD/Views/DrawingsCollectionView.axaml" value="MCCAD/MCCAD.csproj" />
<entry key="MCCAD/Views/LoginView.axaml" value="MCCAD/MCCAD.csproj" />
<entry key="MCCAD/Views/MainWindow.axaml" value="MCCAD/MCCAD.csproj" /> <entry key="MCCAD/Views/MainWindow.axaml" value="MCCAD/MCCAD.csproj" />
<entry key="MCCAD/Views/PositiionControl.axaml" value="MCCAD/MCCAD.csproj" /> <entry key="MCCAD/Views/PositiionControl.axaml" value="MCCAD/MCCAD.csproj" />
<entry key="MCCAD/Views/TeamsView.axaml" value="MCCAD/MCCAD.csproj" />
</map> </map>
</option> </option>
</component> </component>
+11
View File
@@ -21,6 +21,17 @@
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets> <IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets> <PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.14">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.14">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="ReactiveUI.Avalonia" Version="11.4.12" /> <PackageReference Include="ReactiveUI.Avalonia" Version="11.4.12" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+21
View File
@@ -0,0 +1,21 @@
using Microsoft.EntityFrameworkCore;
namespace MCCAD.Models;
public class CadDBContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies()
.UseNpgsql("Host=localhost;User id=postgres;Password=1234;Database=cad_db");
}
public DbSet<User> Users { get; set; }
public DbSet<Drawing> Drawings { get; set; }
public DbSet<Collaborators> Collaborators { get; set; }
public DbSet<Team> Teams { get; set; }
public DbSet<Membership> Memberships { get; set; }
public static readonly CadDBContext Instance = new();
public static User User;
}
+15
View File
@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace MCCAD.Models;
public class Collaborators
{
public int Id { get; set; }
[ForeignKey(nameof(User))]
public int UserId { get; set; }
public virtual User User { get; set; }
[ForeignKey(nameof(Drawing))]
public int DrawingId { get; set; }
public virtual Drawing Drawing { get; set; }
}
+56
View File
@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.CompilerServices;
namespace MCCAD.Models;
public class Drawing: INotifyPropertyChanged
{
private string _name;
[Key]
public int Id { get; set; }
[Required]
public string Name
{
get => _name;
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public string Data { get; set; } = "[]";
[ForeignKey(nameof(User))]
public int UserId { get; set; }
public virtual User User { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastChange { get; set; }
public double Size => double.Round(Data.Length / 1024.0, 2);
public Drawing()
{
CreationDate = DateTime.Now.ToUniversalTime();
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
+14
View File
@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace MCCAD.Models;
public class Membership
{
public int Id { get; set; }
[ForeignKey(nameof(User))]
public int UserId { get; set; }
public virtual User User { get; set; }
[ForeignKey(nameof(Team))]
public int TeamId { get; set; }
public virtual Team Team { get; set; }
}
+20
View File
@@ -0,0 +1,20 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace MCCAD.Models;
public class Team
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; } = "";
[ForeignKey(nameof(Creator))]
public int CreatorId { get; set; }
public virtual User Creator { get; set; }
public int MemberCount => CadDBContext.Instance.Memberships.Count(i => i.TeamId == Id);
public DateTime CreatedAt { get; set; } = DateTime.Now.ToUniversalTime();
}
+11
View File
@@ -0,0 +1,11 @@
namespace MCCAD.Models;
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public int RoleId { get; set; }
public string Description { get; set; } = "";
}
+16
View File
@@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using MCCAD.ViewModels; using MCCAD.ViewModels;
using ReactiveUI;
namespace MCCAD; namespace MCCAD;
@@ -35,3 +36,18 @@ public class ViewLocator : IDataTemplate
return data is ViewModelBase; return data is ViewModelBase;
} }
} }
public class MainViewLocator : IViewLocator
{
private ViewLocator _locator = new ViewLocator();
public IViewFor<TViewModel>? ResolveView<TViewModel>(string? contract = null) where TViewModel : class
{
return _locator.Build(contract) as IViewFor<TViewModel>;
}
public IViewFor? ResolveView(object? instance, string? contract = null)
{
return _locator.Build(instance) as IViewFor;
}
}
+73
View File
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.Json;
using System.Windows.Input;
using MCCAD.Models;
using ReactiveUI;
namespace MCCAD.ViewModels;
public class DrawingViewModel : ViewModelBase, IRoutableViewModel
{
private OvalView _ov;
private PolygonView _pv;
private LineView _lv;
private Drawing _inner;
private static readonly List<IFigureDeserializer> _deserializers =
[new LineView.Deserializer(), new PolygonView.Deserializer(), new OvalView.Deserializer()];
public DrawingViewModel(IScreen hostScreen, Drawing drawing)
{
HostScreen = hostScreen;
_inner = drawing;
LoadFigures();
}
private void LoadFigures()
{
foreach (var figure in JsonSerializer.Deserialize<List<string>>(_inner.Data))
foreach (var deserializer in _deserializers)
{
if (deserializer.TryDeserialize(figure, out var view))
{
Figures.Add(view);
break;
}
}
}
public OvalView Ov
{
get => _ov;
set => this.RaiseAndSetIfChanged(ref _ov, value);
}
public PolygonView Pv
{
get => _pv;
set => this.RaiseAndSetIfChanged(ref _pv, value);
}
public LineView Lv
{
get => _lv;
set => this.RaiseAndSetIfChanged(ref _lv, value);
}
public ObservableCollection<IFigureView> Figures { get; set; } = [];
public string? UrlPathSegment { get; } = Guid.NewGuid().ToString();
public IScreen HostScreen { get; }
public ICommand OnSave => ReactiveCommand.Create(() =>
{
_inner.Data = JsonSerializer.Serialize(Figures.Select(i => i.Save()));
_inner.LastChange = DateTime.Now.ToUniversalTime();
CadDBContext.Instance.SaveChanges();
});
public ICommand Back => ReactiveCommand.Create(() => { HostScreen.Router.NavigateBack.Execute(); }
);
}
@@ -0,0 +1,77 @@
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using MCCAD.Models;
using ReactiveUI;
namespace MCCAD.ViewModels;
public class DrawingsCollectionViewModel : ViewModelBase, IRoutableViewModel
{
public DrawingsCollectionViewModel(IScreen hostScreen)
{
HostScreen = hostScreen;
User = CadDBContext.User;
}
public string? UrlPathSegment { get; } = Guid.NewGuid().ToString();
public IScreen HostScreen { get; }
public User User { get; set; }
public ObservableCollection<Drawing> Drawings { get; set; } = [];
public Drawing? SelectedDrawing { get; set; }
public ICommand Exit => ReactiveCommand.Create(() => HostScreen.Router.NavigateBack.Execute());
public ICommand Open => ReactiveCommand.Create(() =>
{
if (SelectedDrawing != null)
HostScreen.Router.Navigate.Execute(new DrawingViewModel(HostScreen, SelectedDrawing!));
});
public ICommand Delete => ReactiveCommand.Create(() =>
{
if (SelectedDrawing != null)
{
if (SelectedDrawing.Id != 0)
{
CadDBContext.Instance.Drawings.Remove(SelectedDrawing);
CadDBContext.Instance.SaveChanges();
}
Drawings.Remove(SelectedDrawing);
}
});
public ICommand Save => ReactiveCommand.Create(() =>
{
if (SelectedDrawing == null) return;
if (SelectedDrawing.Id == 0)
{
CadDBContext.Instance.Drawings.Add(SelectedDrawing);
}
else
{
CadDBContext.Instance.Drawings.Update(SelectedDrawing);
}
CadDBContext.Instance.SaveChanges();
});
public ICommand Create => ReactiveCommand.Create(() =>
{
var dr = new Drawing();
dr.User = User;
Drawings.Add(dr);
SelectedDrawing = dr;
});
public string SelectedDrawingName
{
get => (SelectedDrawing ?? new Drawing()).Name;
set
{
if (SelectedDrawing != null)
SelectedDrawing.Name = value;
}
}
}
+131 -6
View File
@@ -2,8 +2,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Text.Json;
using Avalonia.Controls.Shapes;
using MCCAD.Models; using MCCAD.Models;
using ReactiveUI; using ReactiveUI;
using Line = MCCAD.Models.Line;
namespace MCCAD.ViewModels; namespace MCCAD.ViewModels;
@@ -17,23 +20,37 @@ public interface IFigureView
public event ShemeUpdate OnNewSheme; public event ShemeUpdate OnNewSheme;
PositionVM Position { get; set; } PositionVM Position { get; set; }
public string Name { get; set; } public string Name { get; set; }
string Save();
}
public interface IFigureDeserializer
{
bool TryDeserialize(string data, out IFigureView view);
} }
public class LineView : ReactiveObject, IFigureView public class LineView : ReactiveObject, IFigureView
{ {
private static int _counter = 1; private static int _counter = 1;
public LineView () { public LineView()
{
Position = new PositionVM Position = new PositionVM
{ {
Chanded = Rebuild Chanded = Rebuild
}; };
Name = "Line " + _counter++; Name = "Line " + _counter++;
} }
public event ShemeUpdate OnNewSheme; public event ShemeUpdate OnNewSheme;
public PositionVM Position { get; set; } = new (); public PositionVM Position { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Save()
{
var data = new Data() { Angle = Angle, Lenght = lenght, Position = Position, Name = Name };
return "line " + JsonSerializer.Serialize(data);
}
private double angle = 45; private double angle = 45;
private int lenght = 10; private int lenght = 10;
@@ -67,7 +84,32 @@ public class LineView : ReactiveObject, IFigureView
shape = shape.Select(i => new IntPoint(i.X, -i.Y)).ToList(); shape = shape.Select(i => new IntPoint(i.X, -i.Y)).ToList();
var yMove = shape.Select(p => p.Y).Min(); var yMove = shape.Select(p => p.Y).Min();
shape = shape.Select(p => new IntPoint(p.X, p.Y - yMove)).ToList(); shape = shape.Select(p => new IntPoint(p.X, p.Y - yMove)).ToList();
OnNewSheme.Invoke(shape, this); OnNewSheme?.Invoke(shape, this);
}
public class Data
{
public string Name { get; set; }
public int Angle { get; set; }
public int Lenght { get; set; }
public PositionVM Position { get; set; }
}
public class Deserializer : IFigureDeserializer
{
public bool TryDeserialize(string data, out IFigureView? view)
{
if (!data.StartsWith("line"))
{
view = null;
return false;
}
var parsed = JsonSerializer.Deserialize<Data>(data.Substring(data.IndexOf(' ') + 1));
view = new LineView()
{ Name = parsed.Name, Lenght = parsed.Lenght, Position = parsed.Position, Angle = parsed.Angle };
return true;
}
} }
} }
@@ -76,13 +118,47 @@ public class PolygonView : ReactiveObject, IFigureView
public event ShemeUpdate OnNewSheme; public event ShemeUpdate OnNewSheme;
public PositionVM Position { get; set; } public PositionVM Position { get; set; }
public string Name { get; set; } public string Name { get; set; }
public class Data
{
public string Name { get; set; }
public int Corners { get; set; }
public int Radius { get; set; }
public PositionVM Position { get; set; }
}
public class Deserializer : IFigureDeserializer
{
public bool TryDeserialize(string data, out IFigureView? view)
{
if (!data.StartsWith("polygon"))
{
view = null;
return false;
}
var parsed = JsonSerializer.Deserialize<Data>(data.Substring(data.IndexOf(' ') + 1));
view = new PolygonView()
{ Name = parsed.Name, radius = parsed.Radius, Position = parsed.Position, corners = parsed.Corners };
return true;
}
}
public string Save()
{
var data = new Data { Corners = corners, Radius = Radius, Position = Position, Name = Name };
return "polygon " + JsonSerializer.Serialize(data);
}
private int corners = 3; private int corners = 3;
private int radius = 5; private int radius = 5;
private int rotate; private int rotate;
private static int _counter = 1; private static int _counter = 1;
public PolygonView() { public PolygonView()
{
Position = new PositionVM Position = new PositionVM
{ {
Chanded = Rebuild Chanded = Rebuild
@@ -163,7 +239,7 @@ public class PolygonView : ReactiveObject, IFigureView
// start = start.Next; // start = start.Next;
//} //}
//DrawFromTo(vecs.First.Value, vecs.Last.Value); //DrawFromTo(vecs.First.Value, vecs.Last.Value);
OnNewSheme.Invoke(shape, this); OnNewSheme?.Invoke(shape, this);
} }
} }
@@ -240,10 +316,58 @@ public class OvalView : ReactiveObject, IFigureView
} }
} }
public event ShemeUpdate OnNewSheme; public event ShemeUpdate? OnNewSheme;
public PositionVM Position { get; set; } public PositionVM Position { get; set; }
public string Name { get; set; } public string Name { get; set; }
public class Data
{
public string Name { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public int Top { get; set; }
public int Bottom { get; set; }
public int Left { get; set; }
public int Right { get; set; }
public PositionVM Position { get; set; }
}
public class Deserializer : IFigureDeserializer
{
public bool TryDeserialize(string data, out IFigureView? view)
{
if (!data.StartsWith("oval"))
{
view = null;
return false;
}
var parsed = JsonSerializer.Deserialize<Data>(data.Substring(data.IndexOf(' ') + 1));
view = new OvalView
{
Name = parsed.Name,
bottom = parsed.Bottom,
top = parsed.Top,
heigh = parsed.Height,
left = parsed.Left,
right = parsed.Right,
Position = parsed.Position,
width = parsed.Width,
};
return true;
}
}
public string Save()
{
var data = new Data
{
Height = heigh, Width = width, Position = Position, Name = Name, Top = top, Bottom = bottom, Left = left,
Right = right
};
return "oval " + JsonSerializer.Serialize(data);
}
public double NormalizedLenth(double x, double y) public double NormalizedLenth(double x, double y)
{ {
var hWidth = width / 2; var hWidth = width / 2;
@@ -261,6 +385,7 @@ public class OvalView : ReactiveObject, IFigureView
}; };
Name = "Oval " + _counter++; Name = "Oval " + _counter++;
} }
public void Rebuild() public void Rebuild()
{ {
int RoundToInt(double value) => (int)Math.Round(value); int RoundToInt(double value) => (int)Math.Round(value);
+38
View File
@@ -0,0 +1,38 @@
using System;
using System.Linq;
using System.Windows.Input;
using MCCAD.Models;
using ReactiveUI;
namespace MCCAD.ViewModels;
public class LoginViewModel : ViewModelBase, IRoutableViewModel
{
public LoginViewModel(IScreen hostScreen)
{
HostScreen = hostScreen;
}
public string? UrlPathSegment { get; } = Guid.NewGuid().ToString();
public IScreen HostScreen { get; }
public bool InvalidData { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public ICommand TryAuth => ReactiveCommand.Create(() =>
{
if (CadDBContext.Instance.Users.FirstOrDefault(x => x.Login == Login && Password == x.Password) is { } user)
{
CadDBContext.User = user;
switch (user.RoleId)
{
case 1:
HostScreen.Router.Navigate.Execute(new DrawingsCollectionViewModel(HostScreen));
break;
case 2:
HostScreen.Router.Navigate.Execute(new TeamsViewModel(HostScreen));
break;
}
}
});
}
+10 -22
View File
@@ -1,35 +1,23 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using MCCAD.Models;
using ReactiveUI; using ReactiveUI;
namespace MCCAD.ViewModels; namespace MCCAD.ViewModels;
public class MainWindowViewModel : ViewModelBase public class MainWindowViewModel : ViewModelBase, IScreen
{ {
private OvalView _ov; public RoutingState Router { get; private set; }
private PolygonView _pv;
private LineView _lv;
public OvalView Ov public MainWindowViewModel()
{ {
get => _ov; Router = new RoutingState();
set => this.RaiseAndSetIfChanged(ref _ov, value); Router.Navigate.Execute(new LoginViewModel(this));
} }
public PolygonView Pv public static string test_data = """
{ [ "oval {\"Name\":\"Oval 1\",\"Height\":10,\"Width\":10,\"Top\":0,\"Bottom\":10,\"Left\":0,\"Right\":10,\"Position\":{\"X\":0,\"Y\":0}}", "polygon {\"Name\":\"Polygon 1\",\"Corners\":7,\"Radius\":32,\"Position\":{\"X\":0,\"Y\":0}}", "line {\"Name\":\"Line 1\",\"Angle\":45,\"Lenght\":10,\"Position\":{\"X\":0,\"Y\":0}}" ]
get => _pv; """;
set => this.RaiseAndSetIfChanged(ref _pv, value);
}
public LineView Lv
{
get => _lv;
set => this.RaiseAndSetIfChanged(ref _lv, value);
}
public int A { get; set; }
public string Greeting { get; set; }
public ObservableCollection<IFigureView> Figures { get; set; } = [];
} }
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Windows.Input;
using DynamicData;
using MCCAD.Models;
using ReactiveUI;
namespace MCCAD.ViewModels;
public class TeamsViewModel : ViewModelBase, IRoutableViewModel
{
private string _searchString = "";
public TeamsViewModel(IScreen hostScreen)
{
HostScreen = hostScreen;
teams = CadDBContext.Instance.Teams.Where(i => i.CreatorId == CadDBContext.User.Id).ToList();
Teams.AddRange(teams);
}
private List<Team> teams;
public ObservableCollection<Team> Teams { get; set; } = [];
public string? UrlPathSegment { get; } = Guid.NewGuid().ToString();
public IScreen HostScreen { get; }
public ICommand Exit => ReactiveCommand.Create(() => HostScreen.Router.NavigateBack.Execute());
public string SearchString
{
get => _searchString;
set
{
_searchString = value;
Teams.Clear();
Teams.AddRange(string.IsNullOrWhiteSpace(_searchString)
? teams
: teams.Where(i => i.Name.Contains(SearchString, StringComparison.CurrentCultureIgnoreCase)));
}
}
}
+156
View File
@@ -0,0 +1,156 @@
<UserControl 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:viewModels="clr-namespace:MCCAD.ViewModels"
xmlns:views="clr-namespace:MCCAD.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MCCAD.Views.DrawingView"
x:DataType="viewModels:DrawingViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />
<ColumnDefinition />
<ColumnDefinition Width="250"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="125" />
</Grid.RowDefinitions>
<StackPanel Margin="5,5,5,5">
<Button Command="{Binding Back}">← Назад</Button>
<HyperlinkButton Click="CreateShape" Tag="Ov">Новый элипс</HyperlinkButton>
<HyperlinkButton Click="CreateShape" Tag="Pv">Новый многоугольник</HyperlinkButton>
<HyperlinkButton Click="CreateShape" Tag="Lv">Новая линия</HyperlinkButton>
<Panel Name="ToolsTab" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ScrollViewer DataContext="{Binding Path=Ov}" IsVisible="False">
<StackPanel>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Размеры">
<StackPanel>
<TextBlock Text="Ширина" />
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Slider Minimum="3" Maximum="50" VerticalAlignment="Center"
Value="{Binding Width}" />
<TextBox x:Name="WStr" Grid.Column="1" Margin="3" MaxLength="4"
Text="{Binding Width}" />
</Grid>
<TextBlock Text="Высота" />
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Slider Minimum="3" Maximum="50" VerticalAlignment="Center"
Value="{Binding Heigth}" />
<TextBox x:Name="HStr" Grid.Column="1" Margin="3" MaxLength="4"
Text="{Binding Heigth}" />
</Grid>
</StackPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Ограничения">
<DockPanel LastChildFill="False" Name="LimitsDP">
<StackPanel DockPanel.Dock="Top">
<Slider Minimum="0" Maximum="{Binding Heigth}" Width="85" Margin="3"
Value="{Binding TopLimit}" />
<TextBlock HorizontalAlignment="Center" Name="TopTB"
Text="{Binding TopLimit, StringFormat={}от {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Bottom">
<Slider Minimum="0" Maximum="{Binding Heigth}" Width="85" Margin="3"
Value="{Binding BottomLimit}" />
<TextBlock HorizontalAlignment="Center" Name="BotTB"
Text="{Binding BottomLimit, StringFormat={}до {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Left">
<Slider Minimum="0" Maximum="{Binding Width}" Width="85" Margin="3"
Value="{Binding LeftLimit}" />
<TextBlock HorizontalAlignment="Center" Name="LeftTB"
Text="{Binding LeftLimit, StringFormat={}от {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Right" Width="85">
<Slider Minimum="0" Maximum="{Binding Width}" Width="85" Margin="3"
Value="{Binding RightLimit}" />
<TextBlock HorizontalAlignment="Center" Name="RightTB"
Text="{Binding RightLimit, StringFormat={}до {0}}" />
</StackPanel>
</DockPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</ScrollViewer>
<ScrollViewer Margin="5" DataContext="{Binding Pv}" IsVisible="False">
<StackPanel>
<TextBlock Text="Сторона" />
<Slider Minimum="5" Maximum="100" VerticalAlignment="Center" Value="{Binding Radius}" />
<TextBlock Text="{Binding Radius}" />
<TextBlock Text="Углы" />
<Slider Minimum="3" Maximum="10" VerticalAlignment="Center" Value="{Binding Corners}" />
<TextBlock Text="{Binding Corners}" />
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</ScrollViewer>
<StackPanel Margin="5" DataContext="{Binding Lv}" IsVisible="False">
<StackPanel>
<TextBlock>Угол</TextBlock>
<Slider Value="{Binding Angle}" Minimum="0" Maximum="90" />
<TextBlock Text="{Binding Angle}" />
<TextBlock>Длина</TextBlock>
<Slider Value="{Binding Lenght}" Minimum="5" Maximum="90" />
<TextBlock Text="{Binding Lenght}" />
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</StackPanel>
</Panel>
</StackPanel>
<StackPanel Grid.Row="1" VerticalAlignment="Top">
<TextBlock x:Name="CurrentBlock" Margin="5" />
<Slider Minimum="50" Maximum="500" TickFrequency="5" ValueChanged="Slider_ValueChanged" Value="100" />
<TextBlock x:Name="ScaleTB" />
<TextBlock x:Name="TotalTB" Text="0" Margin="5" />
</StackPanel>
</Grid>
<ScrollViewer Grid.Column="1" Margin="5,5,5,5" HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible">
<Grid Name="SchemePanel">
<Ellipse x:Name="BaseElipse" Stroke="Black" StrokeThickness="10" Height="150" Margin="15" Width="150"
VerticalAlignment="Top" HorizontalAlignment="Left" IsVisible="False" />
<Canvas Name="SchemeCanvas" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="15" />
</Grid>
</ScrollViewer>
<Grid Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"
RowDefinitions="Auto * Auto">
<TextBlock>Слои</TextBlock>
<ListBox Margin="0,5" Grid.Row="1" SelectionChanged="SetupSelected" ItemsSource="{Binding Figures}"
Name="Layers">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="Auto *">
<TextBox Grid.Column="1" Text="{Binding Name}" HorizontalAlignment="Stretch"></TextBox>
<Border Margin="5" Background="OrangeRed" Width="30"></Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding OnSave}" Grid.Row="2">Сохранить</Button>
</Grid>
</Grid>
</UserControl>
+237
View File
@@ -0,0 +1,237 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using MCCAD.Models;
using MCCAD.ViewModels;
using ReactiveUI.Avalonia;
namespace MCCAD.Views;
public partial class DrawingView : ReactiveUserControl<DrawingViewModel>
{
public Dictionary<IFigureView, Canvas> _cache = new();
public bool mark = true;
private const int BlockSize = 15;
private bool _switching;
public DrawingView()
{
InitializeComponent();
}
protected override void OnDataContextChanged(EventArgs e)
{
if (ViewModel != null)
{
foreach (var viewModelFigure in ViewModel.Figures)
{
viewModelFigure.OnNewSheme += DrawShape;
viewModelFigure.Rebuild();
}
}
}
void DrawShape(List<IntPoint> points, IFigureView view)
{
if (!_cache.TryGetValue(view, out Canvas canvas))
{
canvas = new Canvas();
_cache.Add(view, canvas);
SchemeCanvas.Children.Add(canvas);
}
canvas.Children.Clear();
canvas.Width = points.Select(i => i.X).Max() * BlockSize;
canvas.Height = points.Select(i => i.Y).Max() * BlockSize;
Dictionary<IntPoint, string> markup = new Dictionary<IntPoint, string>();
if (mark)
{
var lined = points.OrderBy(i => i.Y).GroupBy(i => i.Y);
foreach (var l in lined)
{
if (l.Count() < 3)
continue;
var line = l.OrderBy(i => i.X).ToList();
var linePoint = new List<int> { 0 };
for (int i = 0; i < line.Count; i++)
{
if (i != line.Count - 1 && line[i].X != line[i + 1].X - 1)
{
linePoint.Add(i);
linePoint.Add(i + 1);
}
}
linePoint.Add(line.Count - 1);
var linesIds = linePoint.Chunk(2);
foreach (var microLineId in linesIds)
{
var micloLine = line.Skip(microLineId[0]).Take(microLineId[1] - microLineId[0] + 1).ToList();
if (micloLine.Count > 2)
{
markup.Add(micloLine.First(), "←");
markup.Add(micloLine.Last(), "→");
markup.Add(micloLine[micloLine.Count / 2], micloLine.Count.ToString());
}
}
}
lined = points.OrderBy(i => i.X).GroupBy(i => i.X);
foreach (var l in lined)
{
if (l.Count() < 3)
continue;
var line = l.OrderBy(i => i.Y).ToList();
var linePoint = new List<int>() { 0 };
for (int i = 0; i < line.Count; i++)
{
if (i != line.Count - 1 && line[i].Y != line[i + 1].Y - 1)
{
linePoint.Add(i);
linePoint.Add(i + 1);
}
}
linePoint.Add(line.Count - 1);
var linesIds = linePoint.Chunk(2);
foreach (var microLineId in linesIds)
{
var micloLine = line.Skip(microLineId[0]).Take(microLineId[1] - microLineId[0] + 1).ToList();
if (micloLine.Count > 2)
{
if (!markup.TryAdd(micloLine.First(), "↑"))
{
markup[micloLine.First()] = "+";
}
if (!markup.TryAdd(micloLine.Last(), "↓"))
{
markup[micloLine.Last()] = "+";
}
markup.Add(micloLine[micloLine.Count / 2], micloLine.Count.ToString());
}
}
}
}
foreach (var point in points)
{
Border block = new Border();
block.Height = BlockSize;
block.Width = BlockSize;
block.BorderThickness = new Thickness(2);
block.Background = new SolidColorBrush(Colors.LightGray);
block.DataContext = point;
block.PointerEntered += (sender, e) => { CurrentBlock.Text = (sender as Border).DataContext.ToString(); };
if (mark)
{
if (markup.TryGetValue(point, out var text))
block.Child = new TextBlock
{
Text = text, HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center, FontSize = 10
};
}
Canvas.SetLeft(block, (point.X + view.Position.X) * BlockSize);
Canvas.SetTop(block, (point.Y + view.Position.Y) * BlockSize);
canvas.Children.Add(block);
}
canvas.Width = BlockSize * (points.Select(i => i.X).Max() + 5);
canvas.Height = BlockSize * (points.Select(i => i.Y).Max() + 5);
TotalTB.Text = points.Count.ToString();
}
private void Slider_ValueChanged(object? sender, RangeBaseValueChangedEventArgs rangeBaseValueChangedEventArgs)
{
if (SchemeCanvas == null)
return;
var e = rangeBaseValueChangedEventArgs;
SchemeCanvas.RenderTransform = new ScaleTransform() { ScaleX = e.NewValue / 100, ScaleY = e.NewValue / 100 };
ScaleTB.Text = Math.Round(rangeBaseValueChangedEventArgs.NewValue) + "%";
}
private void CreateShape(object? sender, RoutedEventArgs routedEventArgs)
{
try
{
var x = (sender as Control).Tag;
switch (x)
{
case "Ov":
ViewModel.Ov = new OvalView();
ViewModel.Ov.OnNewSheme += DrawShape;
ViewModel.Ov.Rebuild();
ViewModel.Figures.Add(ViewModel.Ov);
SetupVisiblePanel(0);
break;
case "Pv":
ViewModel.Pv = new PolygonView();
ViewModel.Pv.OnNewSheme += DrawShape;
ViewModel.Pv.Rebuild();
ViewModel.Figures.Add(ViewModel.Pv);
SetupVisiblePanel(1);
break;
case "Lv":
ViewModel.Lv = new LineView();
ViewModel.Lv.OnNewSheme += DrawShape;
ViewModel.Lv.Rebuild();
ViewModel.Figures.Add(ViewModel.Lv);
SetupVisiblePanel(2);
break;
}
}
catch (Exception exception)
{
}
}
private void SetupSelected(object? sender, SelectionChangedEventArgs e)
{
if (Layers.SelectedItem == null)
{
return;
}
_switching = true;
switch (Layers.SelectedItem)
{
case OvalView ov:
ViewModel.Ov = ov;
SetupVisiblePanel(0);
break;
case PolygonView pv:
ViewModel.Pv = pv;
SetupVisiblePanel(1);
break;
case LineView lv:
ViewModel.Lv = lv;
SetupVisiblePanel(2);
break;
}
}
private void SetupVisiblePanel(int index)
{
for (var i = 0; i < ToolsTab.Children.Count; i++)
{
ToolsTab.Children[i].IsVisible = i == index;
}
}
}
+44
View File
@@ -0,0 +1,44 @@
<UserControl 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:viewModels="clr-namespace:MCCAD.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MCCAD.Views.DrawingsCollectionView"
x:DataType="viewModels:DrawingsCollectionViewModel">
<Grid RowDefinitions="Auto * Auto" ColumnDefinitions="* *">
<TextBlock Text="{Binding User.Name, StringFormat={}Добро пожаловать {1}}"></TextBlock>
<Button Grid.Column="1" Grid.Row="0" HorizontalAlignment="Right" Command="{Binding Exit}">Выйти</Button>
<ListBox Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Drawings}"
SelectedItem="{Binding SelectedDrawing}"
Grid.Column="0" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" FontSize="20" FontWeight="Bold"></TextBlock>
<TextBlock Text="{Binding Size, StringFormat={}{0} кб.}"></TextBlock>
<DockPanel HorizontalAlignment="Stretch" LastChildFill="False">
<TextBlock DockPanel.Dock="Right"
Text="{Binding LastChange, StringFormat={}Дата последнего изменения: {0}}"
FontSize="18">
</TextBlock>
<TextBlock DockPanel.Dock="Left" Text="{Binding Size, StringFormat={}Дата создания: {0}}"
FontSize="18">
</TextBlock>
</DockPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ScrollViewer Grid.Row="1" Grid.Column="1">
<StackPanel Spacing="10">
<TextBlock>Название</TextBlock>
<TextBox Text="{Binding SelectedDrawingName, Mode=TwoWay}"></TextBox>
<Button Command="{Binding Open}">Открыть</Button>
<Button Command="{Binding Save}">Сохранить</Button>
<Button Background="OrangeRed" Command="{Binding Delete}">Удалить</Button>
</StackPanel>
</ScrollViewer>
<Button Grid.Row="2" Grid.Column="0" Command="{Binding Create}">Создать чертеж</Button>
</Grid>
</UserControl>
@@ -0,0 +1,15 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using MCCAD.ViewModels;
using ReactiveUI.Avalonia;
namespace MCCAD.Views;
public partial class DrawingsCollectionView : ReactiveUserControl<DrawingsCollectionViewModel>
{
public DrawingsCollectionView()
{
InitializeComponent();
}
}
+41
View File
@@ -0,0 +1,41 @@
<UserControl 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:viewModels="clr-namespace:MCCAD.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MCCAD.Views.LoginView"
x:DataType="viewModels:LoginViewModel">
<UserControl.Styles>
<Style Selector="Button.LoginButton">
<Setter Property="Margin" Value="20, 5" />
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Height" Duration="0:0:0.3" />
</Transitions>
</Setter>
</Style>
<Style Selector="TextBlock.AnimateHeight">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Height" Duration="0:0:0.3" />
</Transitions>
</Setter>
</Style>
</UserControl.Styles>
<StackPanel Width="250" Classes="Centred" HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="10">
<TextBlock FontSize="24">Авторизация</TextBlock>
<TextBlock>Введите ваш логин и пароль</TextBlock>
<TextBlock Classes="AnimateHeight" Foreground="Red" IsVisible="{Binding InvalidData}">Логин или пароль неверны</TextBlock>
<TextBox TextChanged="UpdateLoginVisibility" Text="{Binding Login}" Name="LoginText" Watermark="Логин"
HorizontalAlignment="Stretch" />
<TextBox TextChanged="UpdateLoginVisibility" Text="{Binding Password}" Name="PasswordText"
HorizontalAlignment="Stretch" PasswordChar="~"
Watermark="Пароль" />
<Button Height="0" Name="LoginButton" VerticalAlignment="Top" Command="{Binding TryAuth}"
Classes="Long LoginButton" HorizontalAlignment="Center">
Войти
</Button>
</StackPanel>
</UserControl>
+29
View File
@@ -0,0 +1,29 @@
using System;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Avalonia.VisualTree;
using MCCAD.ViewModels;
using ReactiveUI.Avalonia;
namespace MCCAD.Views;
public partial class LoginView : ReactiveUserControl<LoginViewModel>
{
private const double Button_VISIBLE = 30;
public LoginView()
{
InitializeComponent();
}
private void UpdateLoginVisibility(object? sender, TextChangedEventArgs e)
{
LoginButton.Height = string.IsNullOrWhiteSpace(LoginText.Text) ||
string.IsNullOrWhiteSpace(PasswordText.Text) || PasswordText.Text.Length < 6
? 0
: Button_VISIBLE;
}
}
+7 -142
View File
@@ -4,152 +4,17 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:mcd="using:MCCAD.ViewModels"
xmlns:views="clr-namespace:MCCAD.Views"
x:Class="MCCAD.Views.MainWindow" x:Class="MCCAD.Views.MainWindow"
x:DataType="vm:MainWindowViewModel" x:DataType="vm:MainWindowViewModel"
xmlns:rui="clr-namespace:ReactiveUI.Avalonia;assembly=ReactiveUI.Avalonia"
xmlns:mccad="clr-namespace:MCCAD"
Icon="/Assets/avalonia-logo.ico" Icon="/Assets/avalonia-logo.ico"
Title="MCCAD"> Title="MCCAD">
<Grid> <Grid>
<Grid.ColumnDefinitions> <rui:RoutedViewHost Margin="10" Router="{Binding Router}">
<ColumnDefinition Width="250" /> <rui:RoutedViewHost.ViewLocator>
<ColumnDefinition /> <mccad:MainViewLocator />
<ColumnDefinition Width="250"></ColumnDefinition> </rui:RoutedViewHost.ViewLocator>
</Grid.ColumnDefinitions> </rui:RoutedViewHost>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="125" />
</Grid.RowDefinitions>
<StackPanel Margin="5,5,5,5">
<HyperlinkButton Click="CreateShape" Tag="Ov">Новый элипс</HyperlinkButton>
<HyperlinkButton Click="CreateShape" Tag="Pv">Новый многоугольник</HyperlinkButton>
<HyperlinkButton Click="CreateShape" Tag="Lv">Новая линия</HyperlinkButton>
<Panel Name="ToolsTab" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ScrollViewer DataContext="{Binding Path=Ov}" IsVisible="False">
<StackPanel>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Размеры">
<StackPanel>
<TextBlock Text="Ширина" />
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Slider Minimum="3" Maximum="50" VerticalAlignment="Center"
Value="{Binding Width}" />
<TextBox x:Name="WStr" Grid.Column="1" Margin="3" MaxLength="4"
Text="{Binding Width}" />
</Grid>
<TextBlock Text="Высота" />
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Slider Minimum="3" Maximum="50" VerticalAlignment="Center"
Value="{Binding Heigth}" />
<TextBox x:Name="HStr" Grid.Column="1" Margin="3" MaxLength="4"
Text="{Binding Heigth}" />
</Grid>
</StackPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Ограничения">
<DockPanel LastChildFill="False" Name="LimitsDP">
<StackPanel DockPanel.Dock="Top">
<Slider Minimum="0" Maximum="{Binding Heigth}" Width="85" Margin="3"
Value="{Binding TopLimit}" />
<TextBlock HorizontalAlignment="Center" Name="TopTB"
Text="{Binding TopLimit, StringFormat={}от {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Bottom">
<Slider Minimum="0" Maximum="{Binding Heigth}" Width="85" Margin="3"
Value="{Binding BottomLimit}" />
<TextBlock HorizontalAlignment="Center" Name="BotTB"
Text="{Binding BottomLimit, StringFormat={}до {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Left">
<Slider Minimum="0" Maximum="{Binding Width}" Width="85" Margin="3"
Value="{Binding LeftLimit}" />
<TextBlock HorizontalAlignment="Center" Name="LeftTB"
Text="{Binding LeftLimit, StringFormat={}от {0}}" />
</StackPanel>
<StackPanel DockPanel.Dock="Right" Width="85">
<Slider Minimum="0" Maximum="{Binding Width}" Width="85" Margin="3"
Value="{Binding RightLimit}" />
<TextBlock HorizontalAlignment="Center" Name="RightTB"
Text="{Binding RightLimit, StringFormat={}до {0}}" />
</StackPanel>
</DockPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</ScrollViewer>
<ScrollViewer Margin="5" DataContext="{Binding Pv}" IsVisible="False">
<StackPanel>
<TextBlock Text="Сторона" />
<Slider Minimum="5" Maximum="100" VerticalAlignment="Center" Value="{Binding Radius}" />
<TextBlock Text="{Binding Radius}" />
<TextBlock Text="Углы" />
<Slider Minimum="3" Maximum="10" VerticalAlignment="Center" Value="{Binding Corners}" />
<TextBlock Text="{Binding Corners}" />
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</ScrollViewer>
<StackPanel Margin="5" DataContext="{Binding Lv}" IsVisible="False">
<StackPanel>
<TextBlock>Угол</TextBlock>
<Slider Value="{Binding Angle}" Minimum="0" Maximum="90" />
<TextBlock Text="{Binding Angle}" />
<TextBlock>Длина</TextBlock>
<Slider Value="{Binding Lenght}" Minimum="5" Maximum="90" />
<TextBlock Text="{Binding Lenght}" />
<Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="Расположение">
<views:PositionControl DataContext="{Binding Position}" />
</Expander>
</StackPanel>
</StackPanel>
</Panel>
</StackPanel>
<StackPanel Grid.Row="1" VerticalAlignment="Top">
<TextBlock x:Name="CurrentBlock" Margin="5" />
<Slider Minimum="50" Maximum="500" TickFrequency="5" ValueChanged="Slider_ValueChanged" Value="100" />
<TextBlock x:Name="ScaleTB" />
<TextBlock x:Name="TotalTB" Text="0" Margin="5" />
</StackPanel>
</Grid>
<ScrollViewer Grid.Column="1" Margin="5,5,5,5" HorizontalScrollBarVisibility="Visible">
<Grid>
<Ellipse x:Name="BaseElipse" Stroke="Black" StrokeThickness="10" Height="150" Margin="15" Width="150"
VerticalAlignment="Top" HorizontalAlignment="Left" IsVisible="False" />
<Canvas Name="ShemeChanvas" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="15" />
</Grid>
</ScrollViewer>
<StackPanel Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10" Spacing="10">
<TextBlock>Слои</TextBlock>
<ListBox SelectionChanged="SetupSelected" ItemsSource="{Binding Figures}" Name="Layers">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="Auto *">
<TextBox Grid.Column="1" Text="{Binding Name}" HorizontalAlignment="Stretch"></TextBox>
<Border Margin="5" Background="OrangeRed" Width="30"></Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid> </Grid>
</Window> </Window>
-209
View File
@@ -14,217 +14,8 @@ namespace MCCAD.Views;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
public Dictionary<IFigureView, Canvas> _cache = new();
public bool mark = true;
int blockSize = 15;
private bool switching = false;
private MainWindowViewModel DC => DataContext as MainWindowViewModel;
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
} }
protected override void OnDataContextChanged(EventArgs e)
{
}
void DrawShape(List<IntPoint> points, IFigureView view)
{
if (!_cache.TryGetValue(view, out Canvas canvas))
{
canvas = new Canvas();
_cache.Add(view, canvas);
ShemeChanvas.Children.Add(canvas);
}
canvas.Children.Clear();
canvas.Width = points.Select(i => i.X).Max() * blockSize;
canvas.Height = points.Select(i => i.Y).Max() * blockSize;
Dictionary<IntPoint, string> markup = new Dictionary<IntPoint, string>();
if (mark)
{
var lined = points.OrderBy(i => i.Y).GroupBy(i => i.Y);
foreach (var l in lined)
{
if (l.Count() < 3)
continue;
var line = l.OrderBy(i => i.X).ToList();
var linePoint = new List<int> { 0 };
for (int i = 0; i < line.Count; i++)
{
if (i != line.Count - 1 && line[i].X != line[i + 1].X - 1)
{
linePoint.Add(i);
linePoint.Add(i + 1);
}
}
linePoint.Add(line.Count - 1);
var linesIds = linePoint.Chunk(2);
foreach (var microLineId in linesIds)
{
var micloLine = line.Skip(microLineId[0]).Take(microLineId[1] - microLineId[0] + 1).ToList();
if (micloLine.Count > 2)
{
markup.Add(micloLine.First(), "←");
markup.Add(micloLine.Last(), "→");
markup.Add(micloLine[micloLine.Count / 2], micloLine.Count.ToString());
}
}
}
lined = points.OrderBy(i => i.X).GroupBy(i => i.X);
foreach (var l in lined)
{
if (l.Count() < 3)
continue;
var line = l.OrderBy(i => i.Y).ToList();
var linePoint = new List<int>() { 0 };
for (int i = 0; i < line.Count; i++)
{
if (i != line.Count - 1 && line[i].Y != line[i + 1].Y - 1)
{
linePoint.Add(i);
linePoint.Add(i + 1);
}
}
linePoint.Add(line.Count - 1);
var linesIds = linePoint.Chunk(2);
foreach (var microLineId in linesIds)
{
var micloLine = line.Skip(microLineId[0]).Take(microLineId[1] - microLineId[0] + 1).ToList();
if (micloLine.Count > 2)
{
if (!markup.TryAdd(micloLine.First(), "↑"))
{
markup[micloLine.First()] = "+";
}
if (!markup.TryAdd(micloLine.Last(), "↓"))
{
markup[micloLine.Last()] = "+";
}
markup.Add(micloLine[micloLine.Count / 2], micloLine.Count.ToString());
}
}
}
}
foreach (var point in points)
{
Border block = new Border();
block.Height = blockSize;
block.Width = blockSize;
block.BorderThickness = new Thickness(2);
block.Background = new SolidColorBrush(Colors.LightGray);
block.DataContext = point;
block.PointerEntered += (sender, e) => { CurrentBlock.Text = (sender as Border).DataContext.ToString(); };
if (mark)
{
if (markup.TryGetValue(point, out var text))
block.Child = new TextBlock
{
Text = text, HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center, FontSize = 10
};
}
Canvas.SetLeft(block, (point.X + view.Position.X) * blockSize);
Canvas.SetTop(block, (point.Y + view.Position.Y) * blockSize);
canvas.Children.Add(block);
}
canvas.Width = blockSize * (points.Select(i => i.X).Max() + 5);
canvas.Height = blockSize * (points.Select(i => i.Y).Max() + 5);
TotalTB.Text = points.Count.ToString();
}
private void Slider_ValueChanged(object? sender, RangeBaseValueChangedEventArgs rangeBaseValueChangedEventArgs)
{
if (ShemeChanvas == null)
return;
var e = rangeBaseValueChangedEventArgs;
ShemeChanvas.RenderTransform = new ScaleTransform() { ScaleX = e.NewValue / 100, ScaleY = e.NewValue / 100 };
// ShemeChanvas.transf.LayoutTransform = new ScaleTransform() { ScaleX = e.NewValue / 95, ScaleY = e.NewValue / 95 };
ScaleTB.Text = Math.Round(rangeBaseValueChangedEventArgs.NewValue) + "%";
}
private void CreateShape(object? sender, RoutedEventArgs routedEventArgs)
{
try
{
var x = (sender as Control).Tag;
switch (x)
{
case "Ov":
DC.Ov = new OvalView();
DC.Ov.OnNewSheme += DrawShape;
DC.Ov.Rebuild();
DC.Figures.Add(DC.Ov);
SetupVisiblePanel(0);
break;
case "Pv":
DC.Pv = new PolygonView();
DC.Pv.OnNewSheme += DrawShape;
DC.Pv.Rebuild();
DC.Figures.Add(DC.Pv);
SetupVisiblePanel(1);
break;
case "Lv":
DC.Lv = new LineView();
DC.Lv.OnNewSheme += DrawShape;
DC.Lv.Rebuild();
DC.Figures.Add(DC.Lv);
SetupVisiblePanel(2);
break;
}
}
catch (Exception exception)
{
}
}
private void SetupSelected(object? sender, SelectionChangedEventArgs e)
{
if (Layers.SelectedItem == null)
{
return;
}
switching = true;
switch (Layers.SelectedItem)
{
case OvalView ov:
DC.Ov = ov;
SetupVisiblePanel(0);
break;
case PolygonView pv:
DC.Pv = pv;
SetupVisiblePanel(1);
break;
case LineView lv:
DC.Lv = lv;
SetupVisiblePanel(2);
break;
}
}
private void SetupVisiblePanel(int index)
{
for (var i = 0; i < ToolsTab.Children.Count; i++)
{
ToolsTab.Children[i].IsVisible = i == index;
}
}
} }
+28
View File
@@ -0,0 +1,28 @@
<UserControl 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:viewModels="clr-namespace:MCCAD.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MCCAD.Views.TeamsView"
x:DataType="viewModels:TeamsViewModel">
<Grid RowDefinitions="35 Auto * Auto">
<TextBox Text="{Binding SearchString}" Margin="5" Grid.Row="1" HorizontalAlignment="Stretch"></TextBox>
<ListBox Margin="5" ItemsSource="{Binding Teams}" SelectionChanged="SelectingItemsControl_OnSelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<TextBlock DockPanel.Dock="Right"
Text="{Binding MemberCount, StringFormat={}Количество членов: {0}}">
</TextBlock>
<StackPanel DockPanel.Dock="Left">
<TextBlock Text="{Binding Name}" FontSize="20" FontWeight="Bold"></TextBlock>
<TextBlock Text="{Binding CreatedAt, StringFormat={}Создана {0}}"></TextBlock>
</StackPanel>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="3">Создать команду</Button>
</Grid>
</UserControl>
+20
View File
@@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using MCCAD.ViewModels;
using ReactiveUI.Avalonia;
namespace MCCAD.Views;
public partial class TeamsView : ReactiveUserControl<TeamsViewModel>
{
public TeamsView()
{
InitializeComponent();
}
private void SelectingItemsControl_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
}
}