О, я получил то, что не работало должным образом, я установил свойство Canvas.Top для TextBlock, и, возможно, TextBlock обернут в ContentPresenter, который имеет смысл сейчас

аюсь использовать 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 до их добавления на холст?

Редактировать:

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

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

Ответы на вопрос(3)

когда я пытался связать свойство 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>

 ABa22 мая 2012 г., 19:36
О, я получил то, что не работало должным образом, я установил свойство Canvas.Top для TextBlock, и, возможно, TextBlock обернут в ContentPresenter, который имеет смысл сейчас
Решение Вопроса

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

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

Вот полный пример позиционирования только для XAMLItemsControl предметы на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>
 kshahar17 янв. 2011 г., 15:51
Вы правы по поводу этой проблемы с моим примером кода сверху. Тем не менее, есть проблема с фактическим позиционированием - я не могу просто использовать X и Y, потому что они не в экранных координатах. Преобразование зависит от объекта Image и динамического объекта MapData. Чтобы вы посоветовали?
 trapicki23 июн. 2014 г., 14:34
@ Рик Как бы переместитьItemsPresenter это оборачиваетItemsControl? Как бы я получить ссылку на этоItemsPresenter?

ривязке. Преобразователи значений реализуют интерфейс 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, что устраняет необходимость в преобразователе!

 ColinE17 янв. 2011 г., 11:08
Исходя из сложности, я бы посоветовал вам инкапсулировать эту логику в свой класс MyItem. Это даст намного более простой результат.
 kshahar16 янв. 2011 г., 22:57
Спасибо за ответ, пожалуйста, обратите внимание, что метод преобразования не так уж тривиален - он требует еще двух объектов. Я отредактировал свой вопрос, чтобы отразить это.

Ваш ответ на вопрос