2012-02-20 4 views
1

Я новичок в WPF, и я пытаюсь написать интерактивный элемент управления панорамированием с масштабированием. У меня уже есть зум-сковорода изображение, которое, кажется, работает:Выбор пикселя для PanZoomImage

<Border Name="border" ClipToBounds="True"> 
    <Canvas> 
     <Image Name ="image"> 
       Source="{Binding Path=Source}" 
       MouseLeftButtonDown="image_MouseLeftButtonDown" 
       MouseLeftButtonUp="image_MouseLeftButtonUp" 
       MouseMove="image_MouseMove" 
       MouseWheel="image_MouseWheel"> 
     </Image> 
    </Canvas> 
</Border> 

Для мыши и колеса событий я использовал этот пост: http://www.codeproject.com/Articles/168176/Zooming-and-panning-in-WPF-with-fixed-focus

Я пишу интерактивное управление наследованием от ZoomPanImage и добавления события для LeftMouseUp.

public class ClickableImage : PanZoomImage 
{ 
    public event Action<Point> Click; 

    //... 
    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) 
    { 
     base.OnMouseLeftButtonUp(e); 
     // ... all sorts of checks to distinguish click from mouse move 
     if (Click != null) 
     { 
      Click(ControlToImage(mouseUpCoordinates)); 
     } 
    } 

    protected Point ControlToImage(Point controlPixel) 
    { 
     //this is where i am stuck...  
    } 
} 

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

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

Point imagePixel = image.RenderTransform.Inverse.Transform(controlPixel); 

Но это не сработало. Одна из проблем заключается в том, что Transform начинается как Identity, в то время как на самом деле изображение равномерно растягивается до размера элемента управления.

Спасибо, Дина

+0

Убедитесь, что вы установили [Image.Stretch] (http://msdn.microsoft.com/en-us/library/system.windows.controls. image.stretch.aspx) до 'None' перед применением RenderTransform. – Clemens

+0

@ Clemens, Да, я сделал это. Проблема с этим решением заключается в том, что изначально изображение больше окна. Я хочу, чтобы он изменялся в зависимости от размера окна. Поэтому я попытался добавить обратный вызов, который будет вызываться сразу после изменения источника изображения и изменит преобразование. Но это не сработало, потому что, когда обратный вызов назывался image.actualHeight и image.actualWidth, все равно 0 ... – Dina

+0

Когда вы устанавливаете свойство Source (я предполагаю, что в коде позади), вы устанавливаете его в некоторый [ImageSource] (http: //msdn.microsoft.com/en-us/library/system.windows.media.imagesource.aspx)-derived, например BitmapSource. Получите размер изображения из свойств Width и Height ImageSource и соответствующим образом настройте RenderTransform. Во всяком случае, вам нужно избегать автоматического масштабирования, если вы хотите сделать это вручную. – Clemens

ответ

1

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

<Image Name="image" RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="None" 
        Source="{Binding Path=Source}" 
        MouseLeftButtonDown="image_MouseLeftButtonDown" 
        MouseLeftButtonUp="image_MouseLeftButtonUp" 
        MouseMove="image_MouseMove" 
        MouseWheel="image_MouseWheel" 
        Loaded="image_Loaded"> 
        <Image.ContextMenu> 
         <ContextMenu> 
          <MenuItem Header="Fit to window" Click="FitToWindow_MenuItem_Click"></MenuItem> 
         </ContextMenu> 
        </Image.ContextMenu> 
       </Image> 

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

private void FitViewToWindow() 
{ 
    if (Source == null) 
     throw new InvalidOperationException("Source not set"); 

    BitmapSource bitmapSource = Source as BitmapSource; 
    if (bitmapSource == null) 
     throw new InvalidOperationException("Unsupported Image Source Type"); 

    if (border.ActualWidth <= 0 || border.ActualHeight <= 0) 
     return; 

    double scaleX = border.ActualWidth/bitmapSource.PixelWidth; 
    double scaleY = border.ActualHeight/bitmapSource.PixelHeight; 
    double scale = Math.Min(scaleX, scaleY); 

    Matrix m = Matrix.Identity; 
    m.ScaleAtPrepend(scale, scale, 0, 0); 

    double centerX = (border.ActualWidth - bitmapSource.PixelWidth * scale)/2; 
    double centerY = (border.ActualHeight - bitmapSource.PixelHeight * scale)/2; 
    m.Translate(centerX, centerY); 

    image.RenderTransform = new MatrixTransform(m); 
} 

Эта функция должна быть вызвана при загрузке изображения и при изменении источника изображения. Что касается изменения размера окна - пока вы отслеживаете преобразование, вы сможете правильно преобразовать системы координат. Например, вот что я могу сделать для изменения размера окна:

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) 
{ 
    base.OnRenderSizeChanged(sizeInfo); 

    //center the image in the new size 

    if (sizeInfo.PreviousSize.Width <= 0 || sizeInfo.PreviousSize.Height <= 0) 
     return; 

    Matrix m = image.RenderTransform.Value; 

    double offsetX = (sizeInfo.NewSize.Width - sizeInfo.PreviousSize.Width)/2; 
    double offsetY = (sizeInfo.NewSize.Height - sizeInfo.PreviousSize.Height)/2; 

    m.Translate(offsetX, offsetY); 
    image.RenderTransform = new MatrixTransform(m); 
} 
Смежные вопросы