Как сохранить относительное положение элементов WPF на фоновом изображении - PullRequest
1 голос
/ 28 апреля 2009

Я новичок в WPF, поэтому ответ на следующий вопрос может быть очевиден, однако это не для меня. Мне нужно отобразить изображение, на котором пользователи могут устанавливать маркеры (например, вы можете пометить лицо человека на фотографии прямоугольником), однако маркеры должны сохранять свое относительное положение при масштабировании изображения.

В настоящее время я делаю это, используя Canvas и устанавливая ImageBrush в качестве фона. Это отображает изображение, и я могу добавить такие элементы, как Label (в качестве замены для прямоугольника) поверх изображения. Но когда я устанавливаю метку таким образом, ее позиция является абсолютной, и поэтому, когда базовое изображение масштабируется (поскольку пользователь перетаскивает окно больше), Label остается в своей абсолютной позиции (скажем, 100,100) вместо перехода на новую положение, которое держит его "в синхронизации" с базовым изображением.

Короче говоря, когда я ставлю маркер на глаз человека, после масштабирования окна его не должно быть на ухе.

Есть предложения, как это сделать в WPF? Может быть Canvas это неправильный подход в первую очередь? Я мог бы хранить коллекцию маркеров в коде и пересчитывать их положение каждый раз, когда размер окна изменяется, но я надеюсь, что есть способ позволить WPF выполнить эту работу за меня: -)

Мне интересно услышать ваше мнение по этому поводу. Спасибо

Ответы [ 3 ]

4 голосов
/ 28 апреля 2009

Хорошо, похоже, работает. Вот что я сделал:

  1. Написал пользовательский конвертер
  2. Каждый раз, когда пользователь нажимает на холст, я создаю новую метку (позже обмениваюсь ею с 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, потому что это относительное расположение не учитывает поля и т. Д. Мне придется настроить это

2 голосов
/ 28 апреля 2009

В верхней части моей головы вы можете написать класс конвертера, который будет брать процент и возвращать абсолютную позицию. Например, если ваше окно было 200 X 200, и вы поместили метку в 100 X 100, когда вы масштабировали окно до 400 X 400, этикетка останется на том же месте (согласно вашему первоначальному вопросу). Однако, если вы использовали конвертер, чтобы вместо этого вы могли установить положение меток равным 50% размера его родительского контейнера, тогда при масштабировании окна метка будет перемещаться вместе с ним.

Вам также может понадобиться использовать один и тот же конвертер для ширины и высоты, чтобы он также увеличивался в размере, чтобы соответствовать.

Извините за отсутствие деталей, если я получу возможность, я через некоторое время отредактирую это с примером кода.


Отредактировано, чтобы добавить

Этот вопрос дает код для процентного преобразователя.

0 голосов
/ 25 июня 2009

очень очень полезный ответ. Просто измените одну строку, чтобы класс PercentageConverter наследовал IValueConverter. Спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...