Хорошо, похоже, работает. Вот что я сделал:
- Написал пользовательский конвертер
- Каждый раз, когда пользователь нажимает на холст, я создаю новую метку (позже обмениваюсь ею с UserComponent), создаю привязки, используя класс моего конвертера, и выполняю начальные вычисления, чтобы получить относительную позицию холста из абсолютной позиции. указателя мыши
Вот пример кода для конвертера:
public class PercentageConverter : IValueConverter
{
/// <summary>
/// Calculates absolute position values of an element given the dimensions of the container and the relative
/// position of the element, expressed as percentage
/// </summary>
/// <param name="value">Dimension value of the container (width or height)</param>
/// <param name="parameter">The percentage used to calculate new absolute value</param>
/// <returns>parameter * value as Double</returns>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//input is percentage
//output is double
double containerValue = System.Convert.ToDouble(value, culture.NumberFormat);
double perc;
if (parameter is String)
{
perc = double.Parse(parameter as String, culture.NumberFormat);
}
else
{
perc = (double)parameter;
}
double coord = containerValue * perc;
return coord;
}
/// <summary>
/// Calculates relative position (expressed as percentage) of an element to its container given its current absolute position
/// as well as the dimensions of the container
/// </summary>
/// <param name="value">Absolute value of the container (width or height)</param>
/// <param name="parameter">X- or Y-position of the element</param>
/// <returns>parameter / value as double</returns>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//output is percentage
//input is double
double containerValue = System.Convert.ToDouble(value, culture.NumberFormat);
double coord = double.Parse(parameter as String, culture.NumberFormat);
double perc = coord / containerValue;
return perc;
}
}
А вот как вы можете создавать привязки в XAML (обратите внимание, что мой холст объявлен как <Canvas x:Name="canvas" ... >
):
<Label Background="Red" ClipToBounds="True" Height="22" Name="label1" Width="60"
Canvas.Left="{Binding Converter={StaticResource PercentageConverter}, ElementName=canvas, Path=ActualWidth, ConverterParameter=0.25}"
Canvas.Top="{Binding Converter={StaticResource PercentageConverter}, ElementName=canvas, Path=ActualHeight, ConverterParameter=0.65}">Marker 1</Label>
Однако более полезным является создание ярлыков в коде:
private void canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
var mousePos = Mouse.GetPosition(canvas);
var converter = new PercentageConverter();
//Convert mouse position to relative position
double xPerc = (double)converter.ConvertBack(canvas.ActualWidth, typeof(Double), mousePos.X.ToString(), Thread.CurrentThread.CurrentCulture);
double yPerc = (double)converter.ConvertBack(canvas.ActualHeight, typeof(Double), mousePos.Y.ToString(), Thread.CurrentThread.CurrentCulture);
Label label = new Label { Content = "Label", Background = (Brush)new BrushConverter().ConvertFromString("Red")};
//Do binding for x-coordinates
Binding posBindX = new Binding();
posBindX.Converter = new PercentageConverter();
posBindX.ConverterParameter = xPerc;
posBindX.Source = canvas;
posBindX.Path = new PropertyPath("ActualWidth");
label.SetBinding(Canvas.LeftProperty, posBindX);
//Do binding for y-coordinates
Binding posBindY = new Binding();
posBindY.Converter = new PercentageConverter();
posBindY.ConverterParameter = yPerc;
posBindY.Source = canvas;
posBindY.Path = new PropertyPath("ActualHeight");
label.SetBinding(Canvas.TopProperty, posBindY);
canvas.Children.Add(label);
}
По сути, это почти как моя первая идея: использовать относительное положение вместо абсолютного и пересчитывать все позиции при каждом изменении размера, только так, как это делается в WPF. Именно то, что я хотел, спасибо Мартин!
Обратите внимание, однако, , что эти примеры работают только в том случае, если изображение внутри ImageBrush
имеет точно такие же размеры, что и окружающее Canvas
, потому что это относительное расположение не учитывает поля и т. Д. Мне придется настроить это