Как преобразовать позицию X / Y в свойства Canvas Left / Top при использовании ItemsControl - PullRequest
4 голосов
/ 17 января 2011

Я пытаюсь использовать Canvas для отображения объектов, которые имеют "мировое" местоположение (а не "экранное" местоположение). Холст определяется так:

<Canvas Background="AliceBlue">
    <ItemsControl Name="myItemsControl" ItemsSource="{Binding MyItems}">
        <Image x:Name="myMapImage" Panel.ZIndex="-1" />
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Canvas>
                    <TextBlock Canvas.Left="{Binding WorldX}" Canvas.Top="{Binding WorldY}"
                               Text="{Binding Text}"
                               Width="Auto" Height="Auto" Foreground="Red" />
                </Canvas>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Canvas>

MyItem определяется следующим образом:

public class MyItem
{
    public MyItem(double worldX, double worldY, string text)
    {
        WorldX = worldX;
        WorldY = worldY;
        Text = text;
    }
    public double WorldX { get; set; }
    public double WorldY { get; set; }
    public string Text { get; set; }
}

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

Point worldToScreen(double worldX, double worldY)
{
    // Note that the conversion uses an internal m_mapData object
    var size = m_mapData.WorldMax - m_mapData.WorldMin;
    var left = ((worldX - m_currentMap.WorldMin.X) / size.X) * myMapImage.ActualWidth;
    var top = ((worldY - m_currentMap.WorldMin.Y) / size.Y) * myMapImage.ActualHeight;
    return new Point(left, top);
}

В текущей реализации элементы расположены не в том месте, потому что их местоположение не преобразуется в экранные координаты.

Как применить метод worldToScreen к объектам MyItem до их добавления на холст?


Edit:

Я немного запутался, правильно ли я иду, поэтому я написал еще один вопрос: Как использовать WPF для визуализации простого 2D-мира (карта и элементы)

На этот вопрос также есть полезный и полный ответ

Ответы [ 3 ]

7 голосов
/ 17 января 2011

Основная проблема с кодом, который вы представили, заключается в том, что свойства Canvas.Left и Canvas.Top относятся к Canvas, что в DataTemplate для ItemsControl. Это продолжает "сбрасывать" источник. Вместо этого вы можете:

  • удалить Canvas из DataTemplate
  • делает ItemsPanel для ListBox a Canvas
  • поместите ItemsPresenter, который обертывает элементы ItemsControl с Canvas.Top и Canvas.Left
  • убедитесь, что Image и Canvas имеют одинаковые координаты, или переключитесь на использование Canvas

Вот полный пример только для XAML размещения ItemsControl элементов на Canvas с Image позади Canvas:

<Grid>
    <Image x:Name="image" Height="100" Width="Auto" Source="http://thecybershadow.net/misc/stackoverflow.png"/>
    <ItemsControl Name="myItemsControl">
        <ItemsControl.ItemsSource>
            <PointCollection>
                <Point X="10" Y="10"/>
                <Point X="30" Y="30"/>
            </PointCollection>
        </ItemsControl.ItemsSource>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="Text" Foreground="Red"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding X}"/>
                <Setter Property="Canvas.Top" Value="{Binding Y}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Grid>
1 голос
/ 17 января 2011

Вы можете применить это преобразование в конвертере значений в вашей привязке.Преобразователи значений реализуют интерфейс IValueConverter (http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx).. Проблема в том, что для преобразования требуется компонент X и Y вашего элемента. Простым решением этой проблемы будет привязка к MyItem, а не MyItem.WorldX. Вы можетедостигните этого с помощью «Path =.», если затем вы создадите следующий конвертер значений ...

public class CoordinateLeftConverter: IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        MyItem item = value as MyItem;
        return worldToScreen(item.WorldX, item.WorldY).X;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
    }
}

Вы можете использовать его в привязке следующим образом:

<TextBlock Canvas.Left="{Binding Path=.,Converter={StaticResource CoordinateLeftConverter}" ... />

Гдевы создаете экземпляр CoordinateLeftConverter на своей странице. Ресурсы:

<UserControl.Resources>
  <CoordinateLeftConverter x:Key="CoordinateLeftConverter"/> 
</UserControl.Resources>

Затем вам, конечно, потребуется добавить еще один конвертер для свойства Canvas.Top или предоставить ConverterParameter для переключения между свойством X / Yпреобразованная точка.

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

0 голосов
/ 22 мая 2012

У меня была похожая проблема, когда я пытался привязать свойство Canvas.Top к объекту ViewModel, у которого есть свойство CanvasTop, свойство Canvas.Top сначала получит значение, но затем оно каким-то образом сбрасывается и теряет привязкувыражение.Но я немного поработал над кодом здесь.И поскольку я использую Silverlight, свойства ItemsContainerStyle отсутствует, поэтому я использовал взамен ItemsControl.Resources, поэтому с учетом приведенного выше примера мой код выглядит примерно так:

<Grid>
<Image x:Name="image" Height="100" Width="Auto" Source="http://thecybershadow.net/misc/stackoverflow.png"/>
<ItemsControl Name="myItemsControl">
    <ItemsControl.Resources>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding X}"/>
            <Setter Property="Canvas.Top" Value="{Binding Y}"/>
        </Style>
    </ItemsControl.Resources>
    <ItemsControl.ItemsSource>
        <PointCollection>
            <Point X="10" Y="10"/>
            <Point X="30" Y="30"/>
        </PointCollection>
    </ItemsControl.ItemsSource>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="Text" Foreground="Red"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

...