Реализация WPF Snap Grid - PullRequest
       1

Реализация WPF Snap Grid

4 голосов
/ 18 августа 2010

Я пытаюсь реализовать привязку сетки с использованием WPF и холста. Я думаю, что моя математика отключена, потому что элемент UIElement не привязывается к сетке на заднем плане. Ниже представлен xaml, который я использую для создания сетки, и метод, который я использую, чтобы попытаться привязать UIElement к ближайшей линии сетки. Используемый метод запускается, как только срабатывает событие нажатия кнопки мыши. Если это не правильный подход для WPF, может кто-нибудь указать мне правильное направление?

XAML

 <Border x:Name="dragBorder" 
            BorderBrush="Black"
            BorderThickness="1"
            Margin="5"
            CornerRadius="3">
        <Canvas x:Name="dragCanvas"
                            AllowDragging="true"
                            AllowDragOutOfView="False"
                            Height="{Binding ElementName=dragBorder, Path=ActualHeight}"
                            Width="{Binding ElementName=dragBorder, Path=ActualWidth}">
            <Canvas.Background>
                <VisualBrush TileMode="Tile"
                             Viewport="0,0,16,16"
                             ViewportUnits="Absolute"
                             Viewbox="0,0,16,16"
                             ViewboxUnits="Absolute">
                    <VisualBrush.Visual>
                        <Ellipse Fill="#FF000000"
                                 Width="2"
                                 Height="2" />
                    </VisualBrush.Visual>
                </VisualBrush>
            </Canvas.Background>
        </Canvas>
    </Border>

Метод

private void SnapToGrid(UIElement element)
    {
        double xSnap = (Canvas.GetLeft(element) / gridWidth) * gridWidth;
        double ySnap = (Canvas.GetTop(element) / gridWidth) * gridWidth;

        Canvas.SetLeft(element, xSnap);
        Canvas.SetTop(element, ySnap);

        double tempX = Canvas.GetLeft(element);
        double tempY = Canvas.GetTop(element);
    }   

Ответы [ 3 ]

7 голосов
/ 18 августа 2010

Ваша проблема в том, что ваша функция на самом деле ничего не делает. Вы делите на размер сетки, а затем умножаете на размер сетки, так что фактически вы ничего не делаете (2 * 16/16 = 2). То, что вам нужно использовать, это оператор модуля % и отрегулировать положение x / y на основе расстояния от размера вашей сетки.

Вот рабочая функция, которая привязывается влево / вверх, если ближе к левой / верхней линии сетки или вправо / вниз, в противном случае:

private void SnapToGrid( UIElement element ) {
    double xSnap = Canvas.GetLeft( element ) % GRID_SIZE;
    double ySnap = Canvas.GetTop( element ) % GRID_SIZE;

    // If it's less than half the grid size, snap left/up 
    // (by subtracting the remainder), 
    // otherwise move it the remaining distance of the grid size right/down
    // (by adding the remaining distance to the next grid point).
    if( xSnap <= GRID_SIZE / 2.0 )
        xSnap *= -1;
    else
        xSnap = GRID_SIZE - xSnap;
    if( ySnap <= GRID_SIZE / 2.0 )
        ySnap *= -1;
    else
        ySnap = GRID_SIZE - ySnap;

    xSnap += Canvas.GetLeft( element );
    ySnap += Canvas.GetTop( element );

    Canvas.SetLeft( element, xSnap );
    Canvas.SetTop( element, ySnap );
}
1 голос
/ 18 августа 2010

Canvas.GetLeft(element) вернет двойное число, поэтому, даже если gridWidth - целое число, которое будет выполнять двойную арифметику, а деление и умножение будут более или менее отменены.Я думаю, что вы хотите сделать одно из:

double xSnap = Math.Floor(Canvas.GetLeft(element) / gridWidth) * gridWidth;
double xSnap = Math.Ceiling(Canvas.GetLeft(element) / gridWidth) * gridWidth;
double xSnap = Math.Round(Canvas.GetLeft(element) / gridWidth) * gridWidth;

Они округляют результат деления до целого числа и возвращают кратное значение gridWidth.

0 голосов
/ 30 декабря 2016

Идея состоит в том, чтобы убедиться, что положение любого объекта ограничено определенным набором номеров; то есть конечная позиция должна быть округлена и находиться в пределах этого набора.

Набор состоит из всех факторов для произвольного числа, j; j контролирует силу «привязки» и определяет, какие числа появляются в наборе. Новая позиция, опять же, должна состоять только из чисел, которые появляются в наборе.

Например, скажем, исходная позиция объекта (5, 0), и мы хотим переместить его в (16, 23). Давайте продолжим и сделаем снимок 5 : это означает, что положение объекта может состоять только из факторов 5 . Исходная позиция уже попадает в этот набор, а новая позиция - нет.

Чтобы смоделировать «привязку», новая позиция должна упасть либо на (15,20), либо на (20, 25). Нахождение ближайшего множителя 16 и 23 , даст вам правильную точку. В большинстве случаев необходимо округлить результат.

* ** 1023 тысяча двадцать-дв * Пример
//Get original point of object relative to container element
var original_point = e.GetPosition(sender);

//Calculate new x and y position based on mouse
//var new_x = ...
//var new_y = ...

//New position must be multiple of 8    
var snap = 8;

//Get nearest factor of result position
new_x = original_point.X.NearestFactor(snap);
new_y = original_point.Y.NearestFactor(snap);

public static double NearestFactor(this double Value, double Factor)
{
    return Math.Round((Value / Factor), MidpointRounding.AwayFromZero) * Factor;
}

Само собой разумеется, что этот алгоритм можно использовать и при изменении размера объекта, чтобы обеспечить как привязку позиции, так и размера объекта.

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