Archive

Posts Tagged ‘UI’

Fix: Silverlight Media Framework Player VolumeElement out of sync

I was just adding a storable (persistent) Volume property to the MediaPlayerView class used at ClipFlair’s MediaPlayerWindow (connected to the underlying SMF player’s VolumeLevel property), when I realized that after reloading saved state, the SMF player’s Volume control would show a different value than the value set to it (which I could confirm by flipping the MediaPlayer control and looking at its properties on the backpanel that the ClipFlair app features for each component and for the activity container itself).

It seems to be some bug in the SMF logic or in its default template, since the following fix makes the issue disappear. The trick is that at a descendent class from SMFPlayer (like the MediaPlayer class that ClipFlair’s MediaPlayerWindow uses), one can override the OnApplyTemplate method (a standard method in templated XAML-based controls at Silverlight and WPF) and call the UpdateVolumeElement method shown below.

That method temporarily sets the volume to another value than its current one (this is important, just setting again to the same value would be ignored), then set again the current value to force the VolumeElement control’s UI to update.

 

//Project: ClipFlair (http://ClipFlair.codeplex.com)
//Filename: MediaPlayer.cs
//Version: 20130211

using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;

using Microsoft.SilverlightMediaFramework.Core;
using Microsoft.SilverlightMediaFramework.Core.Media;
using Microsoft.SilverlightMediaFramework.Core.Accessibility.Captions;
using Microsoft.SilverlightMediaFramework.Plugins.Primitives;

namespace ClipFlair.MediaPlayer
{

  public class MediaPlayer : SMFPlayer
  {

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      //...
      UpdateVolumeElement(); //patch for SMF bug
    }

    protected void UpdateVolumeElement()
    {
      //patch for SMF to update VolumeElement UI with any already set VolumeLevel
      double volume = VolumeLevel;
      VolumeLevel = (volume == 1) ? 0.9 : 1;
      VolumeLevel = volume;
    }

//...

 

Update: While submitting this as a bug to SMF source site on Codeplex it came to me that I could try setting the VolumeElement’s VolumeLevel value directly at ApplyTemplate and indeed it works:

protected void UpdateVolumeElement() 
{ //patch for SMF to update VolumeElement UI with any already set VolumeLevel VolumeElement.VolumeLevel = VolumeLevel; }

Can’t step-through Silverlight file dialogs with Visual Studio debugger

While stepping through “ShowDialog()” method of OpenFileDialog with Visual Studio 2010 debugger, at the Silverlight code pictured below (for loading a ClipFlair window’s stored options), I got a “Dialogs must be user-initiated” exception. Same behaviour will be shown with SaveFileDialog too, every time you try to step through the “ShowDialog()” method.

This is because of Silverlight’s security model, which doesn’t allow source code to programmatically show a file dialog when an app is running in its default security sandbox (app is not signed with certificate and user hasn’t given consent for it to run in elevated rights mode), unless that code is called from an event handler that handles some user action on the UI (e.g. some button has been clicked by the user).

Obviously, when stepping through with the debugger it loses the user-initiated-action context somehow and considers the debugger as the initiator of the action, thus not allowing the file dialog to be shown when you try to step-through the “ShowDialog()” method of OpenFileDialog or SaveFileDialog.

The only solution I can suggest is to put a breakpoint right after the “ShowDialog()” returns (e.g. at “using” statement in the code below). If you place a breakpoint at any source code row above or at the “ShowDialog” inside the event handler method (“btnLoad_Click” in the code below) it will fail when the debugger tries to go through the “ShowDialog” method, even if you press “Run” after that breakpoint fires to continue.

image

PivotViewer for a rich search experience

For ClipFlair, I’m looking into implementing advanced search using Silverlight PivotViewer

Update: 

You can now check out ClipFlair Gallery in all its PivotViewer with DeepZoom glory.

A really nice example of PivotViewer search is at:

http://www.appletoncompassion.org/VirtualExhibitViewer

This may take long time to load, since its loading 10000 children art DeepZoom image tiles

You can use the mousewheel to zoom in there or the zoom slider at top-right (can also click an item to zoom to it)

See the filters at the left handside, play with them by selecting items there at the various properties to see the images get rearranged to show only ones selected by the filters.

Has also buttons at the top-right to bin the items based on having same values at properties selected

Another good example is Netflix movie catalog:
http://netflixpivot.cloudapp.net/
e.g. select Sort: Genre at top-right and click Graph View button there instead of the default Grid View to see the items get binned by Genre, then start filtering them down from the left handside bar

Also see a SharePoint sites collection search with PivotViewr:
http://www.wssdemo.com/livepivot/

MSDN magazine collection with PivotViewer:
http://pivot.blob.core.windows.net/msdn-magazine/msdnmagazinepivot.htm

Finally, for a nice series of Silverlight PivotViewer programming articles by Tony Champion see:

http://tonychampion.azurewebsites.net/blog/index.php/tag/pivotviewer/

http://pivotviewerlessons.codeplex.com/ (source code)

http://pivotviewer.championds.com/ (live examples)

BTW, people are also working on HTML5 PivotViewer:

http://www.rogernoble.com/2012/02/02/addressing-the-elephant-in-the-room-the-html5-pivotviewer/

http://lobsterpot.com.au/lobsterpot-html5-pivotviewer-now-open-source

http://lobsterpothtml5pv.codeplex.com/

http://lobsterpot.com.au/pivotviewer/api-reference

http://lobsterpot.com.au/pivotviewer/extending-the-html5-pivotviewer

http://pivot.lobsterpot.com.au/html5.htm (Live HTML5 Sample)

http://examples.hubbardone.com/html5/html5pivotviewer.htm (Live HTML5 Sample)

For developers, here are some direct links to .CXML (Collection XML) files:

http://gallery.clipflair.net/collection/activities.cxml
http://gallery.clipflair.net/collection/video.cxml
http://gallery.clipflair.net/collection/images.cxml

http://pivot.blob.core.windows.net/msdn-magazine/msdnmagazine.cxml
http://images.appletoncompassion.org/deepzoomfull/AppletonCompassionFull.cxml
http://www.esrc.ac.uk/Pivot/CXML/Grants/Grants.cxml
http://labs.championds.com/MIX10/MIX10Collection.cxml
http://spscollection.blob.core.windows.net/pivot/SharePoint.cxml
http://www.xpert360.net/SQLBits/Collection.cxml
http://pivot.metia.com/worldcup/wc2010-dz.cxml

By opening the .CXML file URL in a web browser you can see its XML-based structure.

By using ClipFlair Studio‘s Gallery component you can open most of those .CXML URLs and play with the respective collection (you use the wrench button on the Gallery component’s titlebar to turn it over and set the Source URL, then turn over again to see the collection – just wait a bit if it’s big and takes some time to load).  An alternative is to open in your browser a URL of the form http://studio.clipflair.net?gallery=http://spscollection.blob.core.windows.net/pivot/SharePoint.cxml

Not all of these URLs will work in ClipFlair Studio’s Gallery component though, because some servers may be using restrictive access policy files for Silverlight, not allowing a Silverlight app like ClipFlair Studio which is served from http://studio.clipflair.net to download the .CXML file.

HowTo: Remove Skype from Windows taskbar, keep as Taskbar tray icon

An annoying feature of Skype of recent Skype versions is that by default it shows an icon on the Windows taskbar apart from the one at the taskbar tray while you’re signed in. This also means it shows in the ALT+TAB key sequence when switching between open windows, which you might not always want to happen, since you may accidentally switch to it instead of the windows you’re working with at that moment.

If it (also) had an option “Keep Skype in the taskbar during a video conversation” it would be more useful I guess.

Luckily you can turn off that behaviour from Skype’s Tools/Options menu option, by going to Advanced and then Advanced settings part of it.

image

HowTo: Hide Video area of Silverlight Media Framework Player

Since our onoing project ClipFlair is a follow-up of the succesful project LeViS, during its development I’m also looking at covering possible use-cases I infer from feedback entries at LvS application issue tracker on Codeplex (note that ClipFlair is also opensourced on Codeplex at http://ClipFlair.codeplex.com).

One such issue (http://lvs.codeplex.com/workitem/11511) was titled “Allow video hiding while controller is visible for audio only”, so since Silverlight Media Framework (SMF) is used for the media player in ClipFlair, I looked into how to tell it to hide the video area.

Well, it seems that to hide the video and hear just the audio in SMFPlayer one needs to set MediaPresenterElement‘s (a property of SMFPlayer class) MaxWidth and MaxHeight to 0. One can do that at an overriden OnApplyTemplate method in a class descding from SMFPlayer, or could also do it at a custom SMF player template.

Note that setting MediaPresenterElement’s Visibility property to Visibility.Collapsed makes the player not load the video at all, so that one was not an option.

One could define a VideoVisible DependencyProperty at a class descending from SMFPlayer:

    #region VideoVisible

    /// <summary>
    /// VideoVisible Dependency Property
    /// </summary>
    public static readonly DependencyProperty VideoVisibleProperty =
        DependencyProperty.Register("VideoVisible", typeof(bool), 
typeof(MediaPlayer), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnVideoVisibleChanged))); /// <summary> /// Gets or sets the VideoVisible property. /// </summary> public bool VideoVisible { get { return (bool)GetValue(VideoVisibleProperty); } set { SetValue(VideoVisibleProperty, value); } } /// <summary> /// Handles changes to the VideoVisible property. /// </summary> private static void OnVideoVisibleChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) { MediaPlayer target = (MediaPlayer)d; bool oldVideoVisible = (bool)e.OldValue; bool newVideoVisible = target.VideoVisible; target.OnVideoVisibleChanged(oldVideoVisible, newVideoVisible); } /// <summary> /// Provides derived classes an opportunity to handle changes to the
/// VideoVisible property.
/// </summary> protected virtual void OnVideoVisibleChanged(bool oldVideoVisible,
bool newVideoVisible) { MediaPresenterElement.MaxWidth = (newVideoVisible)?
double.PositiveInfinity : 0; MediaPresenterElement.MaxHeight = (newVideoVisible) ?
double.PositiveInfinity : 0; } #endregion

 

At first, to restore the MaxWidth/MaxHeight to show the video area again I tried to set them to double.NaN which didn’t work. Then I checked MaxWidth documentation which was saying:

The maximum width of the element, in device-independent units (1/96th inch per unit). The default value is PositiveInfinity. This value can be any value equal to or greater than 0.0. PositiveInfinity is also valid.

and

String representation of a Double value equal to or greater than 0.0. This is interpreted as a device-independent unit (1/96th inch) measurement. Strings need not explicitly include decimal points. For instance a value of 1 is acceptable.

The same Double range restrictions as mentioned in the Property Value section apply, except that you must use x:Static Markup Extension to set the value to be PositiveInfinity.

Per the 1st quote, one just needs to set MaxWidth or MaxHeight to double.PositiveInfinity (and not double.NaN that I originally expected) to reset it to its default value, that is behave as if a maximum width or height respectively has never been set.

As for the second quote, it basically says that XAML double fields need special treatment in order to specify a positive infinity value (if you ever need to, since you’d usually just skip the MaxWidth/MaxHeight field, unless you need to override some non-default inherited value). You would need to define a XAML namespace to point to the System namespace at mscorlib assembly (library), either at the root control in the XAML or directly where you need it:


<MyNameSpace:MyControl

      xmlns:sys="clr-namespace:System;assembly=mscorlib"

      MaxWidth=”{x:Static sys:Double.PositiveInfinity}”

      MaxHeight=”{x:Static sys:Double.PositiveInfinity}”

      …

/>

BTW, to hide the controller bar (called ControlStrip in SMF) you just set IsControlStripVisible property to false.

HowTo: Scale control arround its center using a render transform

In the context of ClipFlair development, I was recently adding independent scaling (zooming) functionality to its ZUI container’s floating windows (apart from the container zooming functionality) and came across some strange behavior, where the windows seemed to also move apart from getting scaled.

After banging my head a bit I decided to take a closer look at Microsoft’s documentation and noticed the following text at http://msdn.microsoft.com/en-us/library/system.windows.uielement.rendertransformorigin.aspx

"RenderTransformOrigin has a somewhat nonstandard use of the Point structure value, in that the Point does not represent an absolute location in a coordinate system. Instead, values between 0 and 1 are interpreted as a factor for the range of the current element in each x,y axis. For example, (0.5,0.5) will cause the render transform to be centered on the element, or (1,1) would place the render transform at the bottom right corner of the element. NaN is not an accepted value. Values beyond 0 and 1 are also accepted, and will result in more unconventional transform effects. For instance, if you set RenderTransformOrigin to be (5,5), and then apply a RotateTransform, the rotation point will be well outside the bounds of the element itself. The transform will spin your element around in a big circle that originates beyond bottom right. The origin might be somewhere inside its parent element and could possibly be possibly out of frame or view. Negative point values are similar, these will go beyond the top left bounds. Render transforms do not affect layout, and are typically used to animate or apply a temporary effect to an element."

Since ClipFlair’s FloatingWindowHostZUI template uses a Canvas to host its FloatingWindows, I obviously didn’t care about the phrase “Render transforms do not affect layout”, but the phrase “values between 0 and 1 are interpreted as a factor for the range of the current element in each x,y axis” rang a bell immediately.

Misguided by the poor Intellisense info for the RenderTransformOrigin property, I had thought that point was in the control’s coordinate system, and since I wanted to scale the control arround its center, I had used the following erroneous statement:

window.RenderTransformOrigin = new Point(window.ActualWidth/2, window.ActualHeight/2);
window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);

instead of the correct one:

window.RenderTransformOrigin = new Point(0.5, 0.5);
                                                        //scale arround the window center
window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);

That is the range 0 to 1 for x & y coordinates of RenderTransformOrigin refers to the UIElement region, whereas less or greater values are (proportionally) outside of it, useful for example if you want to rotate an object arround an external point with a RotateTransform.

Don’t get puzzled by the expression new ScaleTransform().SetScale(…), it’s a syntax I use for authoring portable source code between WPF and Silverlight (since Silverlight only has a parameter-less constructor for ScaleTransform and anyway WPF doesn’t have a constructor that takes a single parameter for both X and Y scale values).

To sum up, here’s the “Scale” property I added to the FloatingWindow class:

#region public double Scale   
/// <summary> /// Gets or sets current window scale. /// </summary> /// <value>Current scale.</value>
public double Scale {  
get { return (double)GetValue(ScaleProperty); }
  set { SetValue(ScaleProperty, value); } } /// <summary>
/// Identifies the <see cref="FloatingWindow.Scale" /> dependency property.
/// </summary>
/// <value>
/// The identifier for the <see cref="FloatingWindow.Scale" /> dependency property.
/// </value>
public static readonly DependencyProperty ScaleProperty =
    DependencyProperty.Register("Scale", typeof(double), typeof(FloatingWindow),
      new PropertyMetadata(1d, OnScalePropertyChanged));
//Must use 1d here, not 1 (else will get XAMLParseException at runtime)
/// <summary>
/// ScaleProperty PropertyChangedCallback call back static function.
/// </summary>
/// <param name="d">FloatingWindow object whose Scale property is changed.</param>
/// <param name="e">DependencyPropertyChangedEventArgs contains old and new values.</param>
private static void OnScalePropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) {  
FloatingWindow window = (FloatingWindow)d;
  if (window != null)
  { 
    window.RenderTransformOrigin = new Point(0.5, 0.5); //scale arround the window center
    window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);
}
} #endregion

HowTo: Set UI language (CurrentUICulture) in Silverlight

At LvS (the opensource application of LeViS), I’ve been using this (VB.net) code to set the UI language (for example to Greek):

Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("el")

Others seem to have been using (C# – that’s why there is a trailing semicolon):

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("el");

assuming System.Globalization and System.Threading namespaces have been imported.

However, now that I’m building ClipFlair in Silverlight (http://clipflair.codeplex.com), I noticed both GetCultureInfo and CreateSpecificCulture aren’t exposed to Silverlight. Seems one needs for example to modify their App.xaml to use new CultureInfo("el") to do something like below (see parts in bold):

 

using System.Globalization;
using System.Threading;



private void Application_Startup(object sender, StartupEventArgs e)
{
CultureInfo c = new CultureInfo("el");            
Thread.CurrentThread.CurrentCulture = c;            
Thread.CurrentThread.CurrentUICulture = c;



this.RootVisual = new MainPage();        
}

 

Alternatively you can set at each XAML page/control the “language” attribute of the “UserControl” element.

If you want to enable dynamic on-the-fly localization based on end-user selection of UI language at runtime (say via a drop-down box), checkout a nice solution (the “Localizer” one) at:

http://stackoverflow.com/questions/3992007/how-to-switch-ui-culture-of-data-binding-on-the-fly-in-silverlight

Zoom and Pan control for WPF and Silverlight (via WPF compatibility layer)

I’ve managed to combine

http://www.codeproject.com/Articles/85603/A-WPF-custom-control-for-zooming-and-panning

and a cut-down version of it for Silverlight that was out there:

http://www.codeproject.com/Articles/167453/A-Silverlight-custom-control-for-zooming-and-panni

I did it in a way that the Silverlight version is source-code compatible with the WPF version (via a WPF compatibility layer that even implements value coercion), so that both the WPF and the Silverlight projects share the same source code files.

The resulting libraries for WPF and Silverlight work with the existing samples (from the two articles mentioned above) without any changes to them.

Can checkout the latest version (plan to add the samples there too for convenience) under the "Client" subfolder of ClipFlair source-code base at http://clipflair.codeplex.com

Categories: Posts Tags: , , , , ,

HowTo: Load a XAML ResourceDictionary from a .NET assembly

Copying here my answer at:

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/11a42336-8d87-4656-91a3-275413d3cc19

What seems to work for me is the following (copying from the source code of http://ClipFlair.codeplex.com [check out the FlipPanel project under “Client” subfolder])

note

I’m using Build Action = "Page" and Custom Tool="MSBuild:Compile" at the properties of Themes\DropDownTheme.xml and Themes\RotateHorizontalTheme.xaml, as was for Themes\Generic.xaml. Seems to work OK (probably this is faster at runtime compared to setting Build Action to Resource and telling it to not build it)

according to http://stackoverflow.com/questions/145752/what-are-the-various-build-action-settings-in-vs-net-project-properties-and-wh the Build Action = "Page" compiles the XAML into BAML (this seems to also apply to Silverlight 5)

* FlipPanel project, Themes\Generic.xaml

<ResourceDictionary
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:local="clr-namespace:FlipPanel;assembly=FlipPanel">
 
  <ResourceDictionary.MergedDictionaries>
    <ResourceDictionary 
Source="/FlipPanel;component/Themes/RotateHorizontalTheme.xaml" /> </ResourceDictionary.MergedDictionaries>
<Style TargetType="local:FlipPanel"> <Setter Property="Template"
Value="{StaticResource FlipPanel_RotateHorizontalTemplate}"/> </Style> </ResourceDictionary>

* FlipPanel Project, Themes\RotateHorizontalTheme.xaml

<ResourceDictionary
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:local="clr-namespace:FlipPanel;assembly=FlipPanel">
 
  <ControlTemplate x:Key="FlipPanel_RotateHorizontalTemplate" 
TargetType="local:FlipPanel"> <Grid> ... </Grid> </ControlTemplate> <Style x:Key="FlipPanel_RotateHorizontalStyle" TargetType="local:FlipPanel"> <Setter Property="Template"
Value="{StaticResource FlipPanel_RotateHorizontalTemplate}"/> </Style> </ResourceDictionary>

* FlipPanel Project, Themes\DropDownTheme.xaml

<ResourceDictionary
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:local="clr-namespace:FlipPanel;assembly=FlipPanel">
 
  <ControlTemplate x:Key="FlipPanel_DropDownTemplate" 
TargetType="local:FlipPanel"> <Grid> ... </Grid> </ControlTemplate> <Style x:Key="FlipPanel_DropDownStyle" TargetType="local:FlipPanel"> <Setter Property="Template"
Value="{StaticResource FlipPanel_DropDownTemplate}"/> </Style> </ResourceDictionary>

* FlipPanel.Silverlight.Demo project, FlipPanelTest.xaml

<UserControl x:Class="FlipPanelTest.FlipPanelTest"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:flip="clr-namespace:FlipPanel;assembly=FlipPanel" >
 
  <UserControl.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary 
Source="/FlipPanel;component/Themes/RotateHorizontalTheme.xaml" /> <ResourceDictionary
Source="/FlipPanel;component/Themes/DropDownTheme.xaml" /> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White" Width="400"> <Grid.RowDefinitions> <RowDefinition Height="300"></RowDefinition> <RowDefinition Height="300"></RowDefinition> </Grid.RowDefinitions> <flip:FlipPanel x:Name="panel1" Grid.Row="0" BorderBrush="DarkOrange"
BorderThickness="3" CornerRadius="4" Margin="10" Background="White"
Template="{StaticResource FlipPanel_DropDownTemplate}" >
<!-- Style="{StaticResource FlipPanel_DropDownStyle}" -->
<flip:FlipPanel.FrontContent> <StackPanel Margin="6"> <TextBlock TextWrapping="Wrap" Margin="3" FontSize="16"
Foreground="DarkOrange"
>This is the front side of the FlipPanel.</TextBlock> <Button Margin="3" Padding="3" Content="Button One"></Button> <Button Margin="3" Padding="3" Content="Button Two"></Button> <Button Margin="3" Padding="3" Content="Button Three"></Button> <Button Margin="3" Padding="3" Content="Button Four"></Button> </StackPanel> </flip:FlipPanel.FrontContent> <flip:FlipPanel.BackContent> <Grid Margin="6"> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <TextBlock TextWrapping="Wrap" Margin="3" FontSize="16"
Foreground="DarkMagenta"
>This is the back side of the FlipPanel.</TextBlock> <Button Grid.Row="2" Margin="3" Padding="10"
Content="Flip Back to Front" HorizontalAlignment="Center"
VerticalAlignment="Center" Click="cmdFlip1_Click"></Button> </Grid> </flip:FlipPanel.BackContent> </flip:FlipPanel> <flip:FlipPanel x:Name="panel2" Grid.Row="1" BorderBrush="DarkOrange"
BorderThickness="3" CornerRadius="4" Margin="10" Background="White"
Template="{StaticResource FlipPanel_RotateHorizontalTemplate}" >
<!-- Style="{StaticResource FlipPanel_RotateHorizontalStyle}" -->
<flip:FlipPanel.FrontContent> <StackPanel Margin="6"> <TextBlock TextWrapping="Wrap" Margin="3" FontSize="16"
Foreground="DarkOrange"
>This is the front side of the FlipPanel.</TextBlock> <Button Margin="3" Padding="3" Content="Button One"></Button> <Button Margin="3" Padding="3" Content="Button Two"></Button> <Button Margin="3" Padding="3" Content="Button Three"></Button> <Button Margin="3" Padding="3" Content="Button Four"></Button> </StackPanel> </flip:FlipPanel.FrontContent> <flip:FlipPanel.BackContent> <Grid Margin="6"> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <TextBlock TextWrapping="Wrap" Margin="3" FontSize="16"
Foreground="DarkMagenta"
>This is the back side of the FlipPanel.</TextBlock> <Button Grid.Row="2" Margin="3" Padding="10"
Content="Flip Back to Front" HorizontalAlignment="Center"
VerticalAlignment="Center" Click="cmdFlip2_Click"></Button> </Grid> </flip:FlipPanel.BackContent> </flip:FlipPanel>
<flip:FlipPanel x:Name="panel3" Grid.Row="2" BorderBrush="DarkOrange"
BorderThickness="3" CornerRadius="4" Margin="10" Background="White" >
<!-- using default style here -->
<flip:FlipPanel.FrontContent> <StackPanel Margin="6"> <TextBlock TextWrapping="Wrap" Margin="3" FontSize="16"
Foreground="DarkOrange"
>This is the front side of the FlipPanel.</TextBlock> <Button Margin="3" Padding="3" Content="Button One"></Button> <Button Margin="3" Padding="3" Content="Button Two"></Button> <Button Margin="3" Padding="3" Content="Button Three"></Button> <Button Margin="3" Padding="3" Content="Button Four"></Button> </StackPanel> </flip:FlipPanel.FrontContent> <flip:FlipPanel.BackContent> <Grid Margin="6"> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <TextBlock TextWrapping="Wrap" Margin="3" FontSize="16"
Foreground="DarkMagenta"
>This is the back side of the FlipPanel.</TextBlock> <Button Grid.Row="2" Margin="3" Padding="10"
Content="Flip Back to Front" HorizontalAlignment="Center"
VerticalAlignment="Center" Click="cmdFlip2_Click"></Button> </Grid> </flip:FlipPanel.BackContent> </flip:FlipPanel>



</Grid> </UserControl>

 

Note that I decided to prefix names with "FlipPanel_", not sure if there’s some better way (using XAML namespaces somehow) to avoid any conflicts when merging the dictionaries and resolving the resouces with "{StaticResource …}"

Also note that in each theme file I also provide a Style (that sets the corresponding Template property of the FlipPanel conrol) that one can use instead of using the Template directly. At that Style more FlipPanel control properties could be set to values appropriate for that template (the template defines a skeleton and the style dresses the pirate [skeleton] as somebody cleverly pointed out).

Note that Generic.xaml merges and uses the templat from one of the themes. Couldmake copies of files similar to Generic.xaml and reference the same template but with different values in the Style for other properties to make variations without resorting to Copy/Paste when multiple Themes use the same Template but restyle it a bit.

Another important note is that at Generic.xaml you must not use x:Key="FlipPanel_DefaultStyle" or anyother key at the default style, or the command

        public FlipPanel()
        {
            DefaultStyleKey = typeof(FlipPanel);
        }

won’t load the default style (which is needed when you don’t provide a Template or Style value at the consumer XAML (FlipPanelTest.xaml). Probably one can modify it to load a style by name instead of just by type (probably the issue was that it found multiple named styles applying to that type [both FlipPanel_RotateHorizontalStyle and FlipPanel_DefaultStyle] in the Generic.xaml), but removing the Key and using an unnamed style seems to do the trick.

What to do if Generic.xaml doesn’t get loaded for WPF control

Just came across http://wangmo.wordpress.com/2007/09/27/themesgenericxaml/ which gave me a hint on why a WPF control wasn’t getting instantiated correctly when loaded from an external assembly (dll):

to load generic.xaml for WPF, at the start of Properties\AssemblyInfo.cs you need (note this isn’t used/needed in Silverlight):

using System.Windows;

and at the end of Properties\AssemblyInfo.cs you need:

[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

Mind you that if the project doesn’t show a Properties node in Solution Explorer, you have to either make a new project using the correct template (for a WPF custom control), or right click the project, select Properties, then press the Assembly Information button and enter some dummy values, then OK to create the Properties node (which also creates to a Properties subfolder and AssemblyInfo.cs file).

You can expand (drop-down) the special Properties node in solution explorer then to open AssemblyInfo.cs and add the above stuff if missing.

%d bloggers like this: