全景图wp7 mvvm中的静态和动态全景图项目 [英] Static and dynamic panorama items in a panorama wp7 mvvm

查看:76
本文介绍了全景图wp7 mvvm中的静态和动态全景图项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的情况.

ViewModelA {  
    ObservableCollection<Items> ItemsA  
    ObservableCollection<ViewModelB> ViewModelBs
}

将数据上下文设置为ViewModel A的View A ViewA的全景图带有一个列表框,面板,文本块等,并且列表框的项源绑定到ItemsA

View A with the datacontext set to ViewModel A ViewA has a panorama with a listbox, panels, textblocks etc with the items source for the listbox bound to ItemsA

我想在运行时使用通用数据模板(列表框,文本块等)将其他全景项目添加到全景控件..每个全景项目将在运行时绑定到ViewModelBs集合中的每个ViewModelB.

I want to add other panorama items at runtime to the panorama control with a commond data template (listbox, textblock...etc).. Each panorama item will be bound at run time to each ViewModelB in the ViewModelBs collection.

我不反对为此做一些代码隐藏的事情,因为我不是严格的mvvm纯粹主义者. 但是如果我可以指定控件和数据模板并使其工作,那么解决方案可能会很优雅.我是wpf/xaml的新手,并试图通过编写wp7应用程序来突破这些技术.使用mvvm light框架.最终,我希望动态生成的全景图项目/列表框内容在他们将要绑定的viewmodel ...

I am not oppose to doing some codebehind stuff for this as I am not a strict mvvm purist.. but the solution could be elegant if I could specify control and data templates and make this work. I am kind of new to wpf/xaml and trying to break in to those technologies by writing a wp7 app.. using the mvvm light framework.. Eventually, i want my dynamically generated panorama items/list box content fire off relay commands on the viewmodel they are being bound to...

这是我尝试无法成功工作的一些代码段.希望它能提供一些想法.

Here is some code snippet that i tried unsuccessfully to work. Hope it provides some idea..

<phone:PhoneApplicationPage.Resources>
    <Style x:Key="PanoramaItemStyle" TargetType="ContentControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ContentControl">
                    <Grid x:Name="ContentGrid">
                        <controls:PanoramaItem x:Name="ItemLocationPanoramaItem" Header="{Binding TagName}">
                            <StackPanel >
                                <ListBox  x:Name="ItemLocatorsList" ItemsSource="{Binding ItemLocators}" Height="496" SelectedItem="{Binding SelectedItemLocation, Mode=TwoWay}" >
                                    <Custom:Interaction.Triggers>
                                        <Custom:EventTrigger EventName="SelectionChanged">
                                            <GalaSoft_MvvmLight_Command:EventToCommand x:Name="SelectionChangedEvent" Command="{Binding RelativeSource={RelativeSource TemplatedParent},Path=DataContext.GoToEditItemLocatorCommand}" PassEventArgsToCommand="True"/>
                                        </Custom:EventTrigger>
                                    </Custom:Interaction.Triggers>
                                    <ListBox.ItemsPanel>
                                        <ItemsPanelTemplate >
                                            <StackPanel Orientation="Vertical"  ScrollViewer.VerticalScrollBarVisibility="Auto" />
                                        </ItemsPanelTemplate>
                                    </ListBox.ItemsPanel>
                                    <ListBox.ItemTemplate>
                                        <DataTemplate>
                                            <StackPanel> 
                                                    <StackPanel Orientation="Horizontal" Margin="0,0,0,17">
                                                        <StackPanel Width="311">
                                                            <TextBlock Text="{Binding Path=Item.Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextLargeStyle}"/>
                                                            <TextBlock Text="{Binding Path=Location.Description}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                                                        </StackPanel>
                                                    </StackPanel> 
                                            </StackPanel>
                                        </DataTemplate>
                                    </ListBox.ItemTemplate>
                                </ListBox>
                            </StackPanel>
                        </controls:PanoramaItem>
                        <ContentPresenter/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="Foreground" Value="White"/>
    </Style> 
</phone:PhoneApplicationPage.Resources>



背后的代码: 私有LocationGroupsViewModel viewModel = null;

Codebehind: private LocationGroupsViewModel viewModel = null;

    public LocationGroups()
    {
        InitializeComponent(); 
        LocationGroupsPanaroma.DefaultItem = LocationGroupsPanaroma.Items[0];
         viewModel = this.DataContext as LocationGroupsViewModel;
         CreateDynamicPanaromaItems();
    }


    private void CreateDynamicPanaromaItems()
    {
        foreach (Model.LocationGroup group in viewModel.LocationGroups)
        {
            if (group.TotalItems > 0)
            {
                PanoramaItem pi = new PanoramaItem();
                pi.Header = group.Name;
                pi.Orientation = System.Windows.Controls.Orientation.Horizontal;
                ItemLocationListViewModel itemLocationViewModel = viewModel[group.LocationGroupId];
                pi.DataContext = itemLocationViewModel;
                pi.Style = Resources["PanoramaItemStyle"] as Style;
                LocationGroupsPanaroma.Items.Add(pi);

            }
        }

    }

编辑

ViewModel A具有

ViewModel A has

Items collection

Collection of ViewModelBs

panaroma数据上下文设置为viewmodelA

panaroma data context set to viewmodelA

panaroma item - Statitically created in xaml to some Items collection in ViewModelA

    This pan item has a list box



panaroma items --- to be bound to collection of viewmodelbs

    These pan items should each have a listbox which is selectable
                  and  bound to some collection in View Model B and fires commands on    selection         changed to viewModelB. Currently using the galasoft eventtocommand to hook the selection changed on the
    list box to a relay command. The problem is that this eventtommand should have the viewmodel as its data context and the not the collection (bound to the listbox) within viewmodel. 

推荐答案

好的,终于有时间回答这个问题了.提出的解决方案不需要任何代码,仅依靠MVVM概念和数据绑定.

OK, finally got time to answer the question. The proposed solution does not require any code behind and only relys on MVVM concepts and data binding.

从概念上讲,Panorama控件是ItemPresenter(它继承自ItemsPresenter),即,您可以将ItemsSource绑定到包含代表PanoramaItems的项目的列表.

Conceptionally the Panorama control is an ItemPresenter (it inherits from ItemsPresenter), i.e. you can bind the ItemsSource to a list that contains items that repesent your PanoramaItems.

要渲染PanoramaItem,您将必须提供Panorama.HeaderTemplatePanorama.ItemTemplate的模板.模板内的DataContext是表示您的PanoramaItemViewModel.如果此ViewModel包含项目列表,您现在可以使用它来生成您要查找的ListBoxes.

To render your PanoramaItem you will have to provide templates for Panorama.HeaderTemplate and Panorama.ItemTemplate. The DataContext inside the templates is the ViewModel that represents your PanoramaItem. If this ViewModel contains an item list you now can use it to generate the ListBoxes you were looking for.

这是示例...

ViewModelLocator.cs

using GalaSoft.MvvmLight;

namespace WP7Test.ViewModel
{
    public class ViewModelLocator
    {
        private static MainViewModel _main;

        public ViewModelLocator()
        {
    if (ViewModelBase.IsInDesignModeStatic) {
        // Create design time services and viewmodels
    } else {
        // Create run time services and view models
    }
            _main = new MainViewModel();
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public MainViewModel Main
        {
            get
            {
                return _main;
            }
        }
    }
}

MainViewModel.cs

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        this.Items = new ObservableCollection<ItemViewModel>();

        if (IsInDesignMode) {
            // Code runs in Blend --> create design time data.
        } else {
            // Code runs "for real"
        }
        this.LoadData();
    }

    #region [Items]

    public const string ItemsPropertyName = "Items";

    private ObservableCollection<ItemViewModel> _items = default(ObservableCollection<ItemViewModel>);

    public ObservableCollection<ItemViewModel> Items {
        get {
            return _items;
        }
        private set {
            if (_items == value) {
                return;
            }

            var oldValue = _items;
            _items = value;

            RaisePropertyChanged(ItemsPropertyName);
        }
    }

    #endregion

    private void LoadData() {
        this.Items.Add(new ItemViewModel() { LineOne = "runtime one", LineTwo = "Maecenas praesent accumsan bibendum", LineThree = "Facilisi faucibus habitant inceptos interdum lobortis nascetur pharetra placerat pulvinar sagittis senectus sociosqu" });
        this.Items.Add(new ItemViewModel() { LineOne = "runtime two", LineTwo = "Dictumst eleifend facilisi faucibus", LineThree = "Suscipit torquent ultrices vehicula volutpat maecenas praesent accumsan bibendum dictumst eleifend facilisi faucibus" });
        this.Items.Add(new ItemViewModel() { LineOne = "runtime three", LineTwo = "Habitant inceptos interdum lobortis", LineThree = "Habitant inceptos interdum lobortis nascetur pharetra placerat pulvinar sagittis senectus sociosqu suscipit torquent" });

        foreach (var item in Items) {
            for (int i = 0; i < 5; ++i)
                item.Items.Add(new ItemViewModel() { LineOne = "Item " + i, LineTwo = "Maecenas praesent accumsan bibendum" });
        }
    }
}

ItemViewModel.cs

public class ItemViewModel : ViewModelBase
{
    public ItemViewModel() {
        this.Items = new ObservableCollection<ItemViewModel>();

        if (IsInDesignMode) {
            // Code runs in Blend --> create design time data.
        } else {
            // Code runs "for real": Connect to service, etc...
        }
    }

    public override void Cleanup() {
        // Clean own resources if needed

        base.Cleanup();
    }

    #region [LineOne]

    public const string LineOnePropertyName = "LineOne";

    private string _lineOne = default(string);

    public string LineOne {
        get {
            return _lineOne;
        }

        set {
            if (_lineOne == value) {
                return;
            }

            var oldValue = _lineOne;
            _lineOne = value;
            RaisePropertyChanged(LineOnePropertyName);
        }
    }

    #endregion

    #region [LineTwo]

    public const string LineTwoPropertyName = "LineTwo";

    private string _lineTwo = default(string);

    public string LineTwo {
        get {
            return _lineTwo;
        }

        set {
            if (_lineTwo == value) {
                return;
            }

            var oldValue = _lineTwo;
            _lineTwo = value;

            RaisePropertyChanged(LineTwoPropertyName);
        }
    }

    #endregion

    #region [LineThree]

    public const string LineThreePropertyName = "LineThree";

    private string _lineThree = default(string);

    public string LineThree {
        get {
            return _lineThree;
        }

        set {
            if (_lineThree == value) {
                return;
            }

            var oldValue = _lineThree;
            _lineThree = value;
            RaisePropertyChanged(LineThreePropertyName);
        }
    }

    #endregion

    #region [Items]

    public const string ItemsPropertyName = "Items";

    private ObservableCollection<ItemViewModel> _items = default(ObservableCollection<ItemViewModel>);

    public ObservableCollection<ItemViewModel> Items {
        get {
            return _items;
        }
        private set {
            if (_items == value) {
                return;
            }

            var oldValue = _items;
            _items = value;
            RaisePropertyChanged(ItemsPropertyName);
        }
    }

    #endregion
}

MainPage.xaml

<phone:PhoneApplicationPage 
    x:Class="WP7Test.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800" 
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait"  Orientation="Portrait"
    shell:SystemTray.IsVisible="False">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{Binding Main, Source={StaticResource Locator}}">
        <controls:Panorama Title="my application" ItemsSource="{Binding Items}">
            <controls:Panorama.Background>
                <ImageBrush ImageSource="PanoramaBackground.png"/>
            </controls:Panorama.Background>
            <controls:Panorama.HeaderTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding LineOne}"/>
                </DataTemplate>
            </controls:Panorama.HeaderTemplate>
            <controls:Panorama.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Border BorderThickness="0,0,0,1" BorderBrush="White">
                            <TextBlock Text="{Binding LineTwo}" FontSize="28" TextWrapping="Wrap"/>
                        </Border>
                        <Border BorderThickness="0,0,0,1" Margin="0,20" BorderBrush="White">
                            <TextBlock Text="{Binding LineThree}" TextWrapping="Wrap"/>
                        </Border>
                        <ListBox ItemsSource="{Binding Items}">
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel>
                                        <TextBlock Text="{Binding LineOne}" FontSize="24"/>
                                        <TextBlock Text="{Binding LineTwo}" FontSize="18" Margin="24,0,0,5"/>
                                    </StackPanel>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>
                    </StackPanel>
                </DataTemplate>
            </controls:Panorama.ItemTemplate>
        </controls:Panorama>
    </Grid>
    <!--Panorama-based applications should not show an ApplicationBar-->
</phone:PhoneApplicationPage>

编辑-添加其他第一个面板

最后,我了解您要达到的目标!但是,您仍然不需要后面的代码即可!您只需要一个模板...此Blend确实可以为您提供帮助,因为它可以让您提取现有控件的模板...好的,这是所做的更改.

Finally I understand what you trying to achive! However, you still need no code behind to do it! You just need a template ... for this Blend does help you as it lets you extract a template for a exiting control ... ok, here are the changes.

首先,我向MainViewModel添加了一个新属性以显示一些数据:

First I added a new property to the MainViewModel to show some data:

#region [MainPageProperty]

public const string MainPagePropertyPropertyName = "MainPageProperty";
private string _mainPageProperty = "Facilisi faucibus habitant inceptos interdum lobortis nascetur pharetra placerat pulvinar sagittis senectus sociosqu";

public string MainPageProperty {
    get {
        return _mainPageProperty;
    }
    set {
        if (_mainPageProperty == value) {
            return;
        }

        _mainPageProperty = value;
        RaisePropertyChanged(MainPagePropertyPropertyName);
    }
}

#endregion

然后,我使用Blend获得了Panorama控件的模板,并将其插入到controls:Panorama元素中.

Then I used Blend to get the template for the Panorama control and inserted it into the controls:Panorama element.

<controls:Panorama.Template>
    <ControlTemplate TargetType="controls:Panorama">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <controlsPrimitives:PanningBackgroundLayer x:Name="BackgroundLayer" HorizontalAlignment="Left" Grid.RowSpan="2">
                <Border x:Name="background" Background="{TemplateBinding Background}" CacheMode="BitmapCache"/>
            </controlsPrimitives:PanningBackgroundLayer>
            <controlsPrimitives:PanningTitleLayer x:Name="TitleLayer" CacheMode="BitmapCache" ContentTemplate="{TemplateBinding TitleTemplate}" Content="{TemplateBinding Title}" FontSize="187" FontFamily="{StaticResource PhoneFontFamilyLight}" HorizontalAlignment="Left" Margin="10,-76,0,9" Grid.Row="0"/>
            <controlsPrimitives:PanningLayer x:Name="ItemsLayer" HorizontalAlignment="Left" Grid.Row="1">
                <StackPanel Orientation="Horizontal">
                    <controls:PanoramaItem Header="Main panel" Width="432">
                        <TextBlock Text="{Binding ElementName=LayoutRoot, Path=DataContext.MainPageProperty}" TextWrapping="Wrap"/>
                    </controls:PanoramaItem>
                    <ItemsPresenter x:Name="items"/>
                </StackPanel>
            </controlsPrimitives:PanningLayer>
        </Grid>
    </ControlTemplate>
</controls:Panorama.Template>

这里有两个技巧,首先,我插入了一个StacPanel,以允许在controlPrimitives:PanningLayer下的多个名称为ItemsPanel的元素.在此StackPanel中,我移动了ItemsPresenter并添加了另一个PanoramaItem.但是,重要的一件事是设置PanoramaItemWidth属性,否则面板将扩展到所需的房间.

There are two tricks here, first I inserted a StacPanel to have allow for more than one element underneath the controlPrimitives:PanningLayer with the name ItemsPanel. Into this StackPanel I moved the ItemsPresenter and added another PanoramaItem. One thing that is important, though, is to set the Width property of the PanoramaItem, as otherwise the panel will extend to the room that is needed.

另一个技巧是,为了访问DataContext,我必须在绑定中使用ElementName.

The other trick is that in order to get access to the DataContext I had to use the ElementName in the Binding.

希望这显示了MVVM和模板的强大功能!

这篇关于全景图wp7 mvvm中的静态和动态全景图项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆