Static and dynamic panorama items in a panorama wp7 mvvm

后端 未结 1 1657
野趣味
野趣味 2020-12-11 13:23

Here is my situation.

ViewModelA {  
    ObservableCollection ItemsA  
    ObservableCollection ViewModelBs
}
<
相关标签:
1条回答
  • 2020-12-11 13:43

    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.

    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.

    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.

    And here is the sample ...

    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>
    

    Edit - adding additional first panel

    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.

    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
    

    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>
    

    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.

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

    Hope this shows the power of MVVM and Templating!

    0 讨论(0)
提交回复
热议问题