Agregado un nuevo objeto CustomImage

This commit is contained in:
Miguel 2025-02-24 11:37:52 +01:00
parent 5f680b3a7a
commit 5e95459e3e
8 changed files with 235 additions and 41 deletions

View File

View File

View File

@ -343,7 +343,25 @@
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- osBase -->
<!-- File Dialog -->
<xctk:EditorTemplateDefinition TargetProperties="ImagePath">
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Grid.Column="1" Content="..." Width="25" Margin="2,0,0,0"
Click="ImagePathButton_Click" />
</Grid>
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- osBase -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type ObjetosSim:osBase}" />

View File

@ -24,6 +24,7 @@ namespace CtrEditor
private bool _isDraggingCanvas = false;
private Image imagenDeFondo;
internal ObjectManipulationManager _objectManager;
private Rectangle _panningArea;
// Temporizadores y animación
private DispatcherTimer _zoomTimer;
@ -44,6 +45,12 @@ namespace CtrEditor
_objectManager = new ObjectManipulationManager(this, ImagenEnTrabajoCanvas);
_panningArea = new Rectangle
{
Fill = Brushes.Transparent,
IsHitTestVisible = true
};
// Inicializar temporizador de zoom
_zoomTimer = new DispatcherTimer();
_zoomTimer.Interval = TimeSpan.FromMilliseconds(1);
@ -53,9 +60,13 @@ namespace CtrEditor
// Suscribir eventos
this.Loaded += MainWindow_Loaded;
ImagenEnTrabajoScrollViewer.PreviewMouseWheel += ImagenEnTrabajoCanvas_MouseWheel;
ImagenEnTrabajoCanvas.MouseDown += Canvas_MouseDown_Panning;
ImagenEnTrabajoCanvas.MouseMove += Canvas_MouseMove_Panning;
ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning;
_panningArea.MouseDown += Canvas_MouseDown_Panning;
_panningArea.MouseRightButtonDown += Canvas_MouseRightButtonDown;
this.KeyDown += MainWindow_KeyDown;
ImagenEnTrabajoCanvas.MouseEnter += Canvas_MouseEnter;
this.Closed += MainWindow_Closed;
@ -114,20 +125,38 @@ namespace CtrEditor
imagenDeFondo.Source = bitmap;
RenderOptions.SetBitmapScalingMode(imagenDeFondo, BitmapScalingMode.HighQuality);
// Elimina solo los ROIs, no la imagen de fondo
// Actualizar dimensiones del canvas
ImagenEnTrabajoCanvas.Width = bitmap.Width;
ImagenEnTrabajoCanvas.Height = bitmap.Height;
// Configurar el área de panning
_panningArea.Width = bitmap.Width * 4;
_panningArea.Height = bitmap.Height * 4;
// Posicionar el área de panning centrada respecto a la imagen
Canvas.SetLeft(_panningArea, -bitmap.Width / 4);
Canvas.SetTop(_panningArea, -bitmap.Height / 4);
Canvas.SetZIndex(_panningArea, -1); // Asegurar que está detrás de todo
// Asegurarse de que el área de panning está en el canvas
if (!ImagenEnTrabajoCanvas.Children.Contains(_panningArea))
{
ImagenEnTrabajoCanvas.Children.Add(_panningArea);
}
// Posicionar la imagen
Canvas.SetLeft(imagenDeFondo, 0);
Canvas.SetTop(imagenDeFondo, 0);
// Eliminar solo los ROIs, no la imagen de fondo ni el área de panning
for (int i = ImagenEnTrabajoCanvas.Children.Count - 1; i >= 0; i--)
{
if (ImagenEnTrabajoCanvas.Children[i] is not Image)
var child = ImagenEnTrabajoCanvas.Children[i];
if (child != imagenDeFondo && child != _panningArea)
{
ImagenEnTrabajoCanvas.Children.RemoveAt(i);
}
}
ImagenEnTrabajoCanvas.Width = bitmap.Width;
ImagenEnTrabajoCanvas.Height = bitmap.Height;
Canvas.SetLeft(imagenDeFondo, 0);
Canvas.SetTop(imagenDeFondo, 0);
}
private void Canvas_MouseUp_Panning(object sender, MouseButtonEventArgs e)
@ -151,9 +180,10 @@ namespace CtrEditor
{
if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas)
{
// Verificar si el clic fue en el canvas, imagen de fondo o en el rectángulo de selección
if (e.Source == ImagenEnTrabajoCanvas ||
// Permitir el panning cuando se hace clic en el área de panning o en la imagen
if (e.Source == _panningArea ||
e.Source == imagenDeFondo ||
e.Source == ImagenEnTrabajoCanvas ||
(e.Source is Rectangle rect && rect == _objectManager._selectionRectangle))
{
ImagenEnTrabajoCanvas.Focus();
@ -173,7 +203,7 @@ namespace CtrEditor
{
_isDraggingCanvas = true;
_lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
ImagenEnTrabajoCanvas.CaptureMouse();
ImagenEnTrabajoCanvas.CaptureMouse(); // Importante: siempre capturar en el Canvas principal
Mouse.OverrideCursor = Cursors.Hand;
if (DataContext is MainViewModel viewModel)
@ -223,7 +253,7 @@ namespace CtrEditor
double minZoomFactor = Math.Min(
ImagenEnTrabajoScrollViewer.ViewportWidth / ImagenEnTrabajoCanvas.ActualWidth,
ImagenEnTrabajoScrollViewer.ViewportHeight / ImagenEnTrabajoCanvas.ActualHeight);
ImagenEnTrabajoScrollViewer.ViewportHeight / ImagenEnTrabajoCanvas.ActualHeight)/1.5;
_targetZoomFactor = e.Delta > 0 ?
_initialZoomFactor * 1.4 :
@ -310,6 +340,25 @@ namespace CtrEditor
}
}
private void ImagePathButton_Click(object sender, RoutedEventArgs e)
{
var dlg = new Ookii.Dialogs.Wpf.VistaOpenFileDialog
{
Filter = "Image files (*.png;*.jpg;*.bmp)|*.png;*.jpg;*.bmp|All files (*.*)|*.*"
};
if (dlg.ShowDialog() == true)
{
// El DataContext de este botón es el PropertyItem asociado
if ((sender as Button)?.DataContext is Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
{
propertyItem.Value = dlg.FileName;
}
}
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
// Only force canvas focus if PanelEdicion doesn't have focus

View File

@ -259,19 +259,32 @@ namespace CtrEditor
return rect;
}
private void AddResizeHandles(FuncionesBase.MutableRect rectBox, double rectSize,
private void AddResizeHandles(FuncionesBase.MutableRect rectBox, double defaultRectSize,
Cursor rotationCursorRx, Cursor rotationCursorSx)
{
// Calcular el tamaño apropiado para los manejadores basado en el tamaño del objeto
double minObjectDimension = Math.Min(rectBox.Width, rectBox.Height);
double rectSize = Math.Min(defaultRectSize, minObjectDimension / 3);
// Asegurar un tamaño mínimo para los manejadores
rectSize = Math.Max(rectSize, 6);
// Calcular el offset para posicionar los manejadores fuera del rectángulo
double offset = rectSize / 2;
var positions = new List<(Point Position, string Tag, Cursor Cursor, Brush Stroke)>
{
(new Point(rectBox.Left, rectBox.Top), "TopLeft", Cursors.Arrow, Brushes.Gray),
(new Point(rectBox.Right, rectBox.Top), "TopRight", rotationCursorRx, Brushes.Red),
(new Point(rectBox.Left, rectBox.Bottom), "BottomLeft", rotationCursorSx, Brushes.DarkRed),
(new Point(rectBox.Right, rectBox.Bottom), "BottomRight", Cursors.SizeNWSE, Brushes.Blue),
(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Top), "TopCenter", Cursors.Arrow, Brushes.Gray),
(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Bottom), "BottomCenter", Cursors.SizeNS, Brushes.Blue),
(new Point(rectBox.Left, rectBox.Top + rectBox.Height / 2), "CenterLeft", rotationCursorRx, Brushes.Red),
(new Point(rectBox.Right, rectBox.Top + rectBox.Height / 2), "CenterRight", Cursors.SizeWE, Brushes.Blue)
// Esquinas - mover hacia afuera del rectángulo
(new Point(rectBox.Left - offset, rectBox.Top - offset), "TopLeft", Cursors.Arrow, Brushes.Gray),
(new Point(rectBox.Right + offset, rectBox.Top - offset), "TopRight", rotationCursorRx, Brushes.Red),
(new Point(rectBox.Left - offset, rectBox.Bottom + offset), "BottomLeft", rotationCursorSx, Brushes.DarkRed),
(new Point(rectBox.Right + offset, rectBox.Bottom + offset), "BottomRight", Cursors.SizeNWSE, Brushes.Blue),
// Centros de los bordes - mover hacia afuera del rectángulo
(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Top - offset), "TopCenter", Cursors.Arrow, Brushes.Gray),
(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Bottom + offset), "BottomCenter", Cursors.SizeNS, Brushes.Blue),
(new Point(rectBox.Left - offset, rectBox.Top + rectBox.Height / 2), "CenterLeft", rotationCursorRx, Brushes.Red),
(new Point(rectBox.Right + offset, rectBox.Top + rectBox.Height / 2), "CenterRight", Cursors.SizeWE, Brushes.Blue)
};
foreach (var (Position, Tag, Cursor, Stroke) in positions)

View File

@ -0,0 +1,13 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucCustomImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.DataContext>
<vm:osCustomImage />
</UserControl.DataContext>
<Image Source="{Binding ImageSource_oculta}"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}" Stretch="Fill" />
</UserControl>

View File

@ -0,0 +1,94 @@
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CtrEditor.FuncionesBase;
using System.ComponentModel;
using Newtonsoft.Json;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// A custom image control that can display an image from a file path
/// </summary>
public partial class osCustomImage : osBase, IosBase
{
public static string NombreClase()
{
return "Custom Image";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ImageSource_oculta))]
[property: Description("Path to the image file")]
[property: Category("Image:")]
private string imagePath;
[JsonIgnore]
[ObservableProperty]
public ImageSource imageSource_oculta;
partial void OnImagePathChanged(string value)
{
if (!string.IsNullOrEmpty(value))
{
ImageSource_oculta = ImageFromPath(value);
}
}
public osCustomImage()
{
Ancho = 0.30f;
Alto = 0.30f;
}
public override void ucLoaded()
{
base.ucLoaded();
if (!string.IsNullOrEmpty(ImagePath))
{
ImageSource_oculta = ImageFromPath(ImagePath);
}
}
}
public partial class ucCustomImage : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucCustomImage()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Highlight(bool State) { }
public ZIndexEnum ZIndex_Base()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -96,45 +96,52 @@ namespace CtrEditor.ObjetosSim
public static void CargarPropiedadesosDatos(object selectedObject, PropertyGrid propertyGrid)
{
// Clear previous properties
// Limpia las propiedades previas
propertyGrid.SelectedObject = null;
propertyGrid.PropertyDefinitions.Clear();
// Add properties dynamically based on attributes
// Obtén las propiedades del objeto (excluyendo las que tengan HiddenAttribute)
var properties = TypeDescriptor.GetProperties(selectedObject)
.Cast<PropertyDescriptor>()
.Where(prop => !prop.Attributes.OfType<HiddenAttribute>().Any()).OrderBy(prop => prop.Name);
.Where(prop => !prop.Attributes.OfType<HiddenAttribute>().Any())
.OrderBy(prop => prop.Name);
foreach (var property in properties)
{
if (property.Name.EndsWith("_oculto"))
continue;
var displayNameAttr = property.Attributes.OfType<DisplayNameAttribute>().FirstOrDefault();
var propertyDefinition = new PropertyDefinition
{
TargetProperties = new[] { property.Name }
TargetProperties = new[] { property.Name },
DisplayName = displayNameAttr != null
? displayNameAttr.DisplayName
: property.Name.Replace("_", " ")
};
if (!property.Name.EndsWith("_oculto"))
// Aquí se añaden todas las propiedades; para "ImagePath" no se requiere
// ninguna configuración adicional, pues el EditorTemplateDefinition en XAML se
// aplicará automáticamente.
if (property.PropertyType == typeof(double) ||
property.PropertyType == typeof(float) ||
property.PropertyType == typeof(string) ||
property.PropertyType == typeof(int) ||
property.PropertyType == typeof(bool) ||
property.PropertyType == typeof(osBase) ||
property.PropertyType == typeof(osVMmotorSim) ||
property.PropertyType == typeof(osBuscarCoincidencias) ||
property.PropertyType == typeof(Color))
{
if (displayNameAttr != null)
{
propertyDefinition.DisplayName = displayNameAttr.DisplayName;
}
else
{
propertyDefinition.DisplayName = property.Name.Replace("_", " ");
}
if (property.PropertyType == typeof(double) || property.PropertyType == typeof(float) || property.PropertyType == typeof(string) || property.PropertyType == typeof(int) ||
property.PropertyType == typeof(bool) || property.PropertyType == typeof(osBase) ||
property.PropertyType == typeof(osVMmotorSim) || property.PropertyType == typeof(osBuscarCoincidencias) || property.PropertyType == typeof(Color) )
{
propertyGrid.PropertyDefinitions.Add(propertyDefinition);
}
propertyGrid.PropertyDefinitions.Add(propertyDefinition);
}
}
propertyGrid.SelectedObject = selectedObject;
}
}
}