Showing posts with label WPF. Show all posts
Showing posts with label WPF. Show all posts

Tuesday, November 02, 2010

Timeline – or restyling controls

One nice thing with WPF that is slightly different is that the controls are lookless. This means that you can re-style or apply a different template altogether if the behavior of a standard control is what you want, but the looks are not.

A real world example: At work we recently needed to show a list of items with associated timestamps, in a way so that the time differences between them are easily determined at a glance.

Thanks to the lookless controls of WPF I was able to solve this simply by using a ListBox with re-templated ListBoxItems giving the following result:

image

The style is defined as follows:

        <Style TargetType="ListBoxItem" x:Key="TimelineStyle">
            <
Setter Property="SnapsToDevicePixels" Value="true"/>
            <
Setter Property="OverridesDefaultStyle" Value="true"/>
            <
Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <
Setter Property="Template">
                <
Setter.Value>
                    <
ControlTemplate TargetType="ListBoxItem">
                        <
StackPanel Orientation="Vertical"> <Border
                           
Name="Border"
                           
Padding="2"
                           
CornerRadius="2"
                           
BorderThickness="2"
                           
SnapsToDevicePixels="true">
                                <
ContentPresenter />
                            </
Border>
                            <
Grid>
                                <
Rectangle Width="2" Stroke="LightBlue"
                                          
Height="{Binding TimeToNext, Converter={StaticResource HeightConverter}}"
                                          
VerticalAlignment="Center"
                                          
HorizontalAlignment="Center"/>
                                <
TextBlock Text="{Binding TimeToNext, Converter={StaticResource DurationConverter}}"
                                          
Background="White"
                                          
VerticalAlignment="Center"
                                          
HorizontalAlignment="Center"
                                          
FontSize="8"
                                          
Foreground="Gray"/>
                            </
Grid>
                        </
StackPanel>
                        <
ControlTemplate.Triggers>
                            <
Trigger Property="IsSelected"
                                    
Value="true">
                                <
Setter TargetName="Border"
                                       
Property="BorderBrush"
                                       
Value="Blue"/>
                            </
Trigger>
                        </
ControlTemplate.Triggers>
                    </
ControlTemplate>
                </
Setter.Value>
            </
Setter>
        </
Style>

and and the listbox uses the style like this:

<ListBox ItemsSource="{Binding DataItems}" ItemContainerStyle="{StaticResource TimelineStyle}" />



The style uses two converters, but those should be fairly trivial.



Setting FocusVisualStyle to {x:Null} is done to hide the indication of keyboard focus, as unless it is fixed properly, will be more confusing than helpful. Fixing it properly is left as an exercise for the reader.

Thursday, January 21, 2010

WPF project building inside of Visual Studio but not with MSBuild/TFSBuild

At work, I recently run into a strange build error when building from MSBuild and TFSBuild while the same solution built inside of Visual Studio 2008 just fine.

The error message was:

error MC3015: The attached property '?' is not defined on '?' or one of its base classes.

(obviously with real names instead of the questionmarks)

Feeding the above into google provided the following solution to the issue:

One of the differences between building in VS and command line is that a WPF build in VS defaults to

<AlwaysCompileMarkupFilesInSeparateDomain>true</AlwaysCompileMarkupFilesInSeparateDomain>.
Outside of VS, the default is false.

(Answer by Rob Relyea)

Adding that to the relevant .csproj made the project build successfully.

Saturday, November 08, 2008

Debugging WPF databinding issues

Lately due to switching teams and project I've been doing much more WPF than before. I cannot say I have fully mastered the learning curve yet but I'm getting better and it is an interesting journey.

Occasionally the data bindings does not work as expected and then it is nice to get more information about them in order to figure out what went wrong.

The following is useful when you want to know why a particular binding is misbehaving (or rather you want to know where you screwed up...)

xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"



ItemsSource="{Binding <snip>, diagnostics:PresentationTraceSources.TraceLevel=High}"


The above information and MUCH more is presented by Beatriz Stollnitz



(This post is also a 'reminder to self')

Monday, January 14, 2008

Maximizing a WPF Window to second monitor

I'm trying to develop and application that utilizes a maximized window on the second monitor.

There's a few issues here. The first is that WPF doesn't offer the same information as WinForms about monitor configuration. WinForms has SystemInformation.MonitorCount and Screen.AllScreens[i].WorkingArea. WPF has SystemParameters.PrimaryScreen[Width/Height] and SystemParameters.VirtualScreen[Width/Height] which almost works (as long as you don't have more than two monitors) but not really. So back to using WinForms API...

Secondly, the obvious solution to display a form maximized on second screen was to me the following:

                fullScreenWindow.WindowStartupLocation = WindowStartupLocation.Manual;

Debug.Assert(System.Windows.Forms.SystemInformation.MonitorCount > 1);

System.Drawing.Rectangle workingArea = System.Windows.Forms.Screen.AllScreens[1].WorkingArea;
fullScreenWindow.Left = workingArea.Left;
fullScreenWindow.Top = workingArea.Top;
fullScreenWindow.Width = workingArea.Width;
fullScreenWindow.Height = workingArea.Height;
fullScreenWindow.WindowState = WindowState.Maximized;
fullScreenWindow.WindowStyle = WindowStyle.None;
fullScreenWindow.Topmost = true;
fullScreenWindow.Show();

However, when you try that you'll end up with a topmost maximized window on your primary display, which was not what we wanted.


Turns out that we cannot maximize the window until it's loaded. So by hooking the Loaded event of fullScreenWindow and handling the event along the lines of:

        private void Window_Loaded(object sender, RoutedEventArgs e) {
WindowState = WindowState.Maximized;
}

...it works.


Oh, and you want to make certain that you are running a dual monitor configuration so you do not put the window somewhere where it is off-screen.

Thursday, September 13, 2007

Enumerating XAML (BAML) files in an Assembly

I wanted to play around with XAML Pages, and wanted an application that automagically showed me a list of all included (embedded) XAML files in an assembly without me doing more than adding the XAML file to the project.

This turned out to be tricky until you got it...

Well, it is simple when you know how to do it, but until then you'll have some googling to do. (At least that is  my experience)

Basically, a XAML file with a build action of Page or Resource ends up in a .resources file named <assemblyname>.g.resources. This .resources file contains the binary version of the XAML file (BAML).

Enumerating the contents of a .resources file can be done using the System.Resources.ResourceReader class as follows:

using (ResourceReader reader = new ResourceReader("Foo.g.resources"))
{
foreach (DictionaryEntry entry in reader)
{
Console.WriteLine(entry.Key);
}
}

So all that's left to do is to create the application, (and no, it ain't pretty but it works):


MainForm.xaml:

<Window x:Class="AutoToc.MainForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=system"
Title="AutoToc" Height="400" Width="600" Loaded="FormLoaded"
>
<
Window.Resources>
<
HierarchicalDataTemplate DataType="{x:Type sys:Uri}">
<
BulletDecorator VerticalAlignment="Center">
<
BulletDecorator.Bullet>
<
Ellipse Fill="BlueViolet" Width="5" Height="5"
VerticalAlignment="Center"/>
</
BulletDecorator.Bullet>
<
TextBlock Text="{Binding Path=OriginalString}"
Margin="5,0,0,0" VerticalAlignment="Center" />
</
BulletDecorator>
</
HierarchicalDataTemplate>
</
Window.Resources>
<
Grid>
<
Grid.ColumnDefinitions>
<
ColumnDefinition Width="200"/>
<
ColumnDefinition Width="Auto"/>
<
ColumnDefinition Width="*"/>
</
Grid.ColumnDefinitions>
<
TreeView Grid.Column="0" Name="tocTree"
ItemsSource="{Binding}"
SelectedItemChanged="tocTreeSelectedItemChanged"></TreeView>
<
GridSplitter Grid.Column="1" Width="2" HorizontalAlignment="Left"
VerticalAlignment="Stretch"/>
<
Frame Name="contentFrame" Grid.Column="2" NavigationUIVisibility="Hidden"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</
Grid>
</
Window>

and the interesting parts of the code behind:

private void FormLoaded(object sender, RoutedEventArgs e)
{
// UriCollection inherits ObservableCollection<Uri>
UriCollection pages = new UriCollection();
Assembly asm = Assembly.GetExecutingAssembly();
Stream stream = asm.GetManifestResourceStream(asm.GetName().Name + ".g.resources");

DataContext = pages;
using (ResourceReader reader = new ResourceReader(stream))
{
foreach (DictionaryEntry entry in reader)
{
// for some curious reason, we get "cannot locate resource"
// if we leave the .baml extension, it needs to be .xaml !?!
pages.Add(new Uri(((string)entry.Key).Replace(".baml", ".xaml"),
UriKind.Relative));

}
}
}

private void tocTreeSelectedItemChanged(object sender, RoutedEventArgs e)
{
contentFrame.Source = tocTree.SelectedItem as Uri;
}

That was easy, wasn't it?


Obviously this is not production quality code but more a proof of concept, and yes, I do use it for playing around with the 3D samples...


Obviously, it would be better to filter out non-Page xaml files, such as mainform ...

Thursday, September 06, 2007

DiffuseMaterial.Color vs DiffuseMaterial.Brush

I've been getting my feet wet with 3D in WPF lately, as I've been reading the new Petzold book 3D Programming for Windows.

It's too early to give any review of the book yet, but back to the subject matter:

As I was trying the examples out I was just getting emptiness !?! I suspected I had screwed up either the MeshGeometry.Positions and/or camera directions so those I checked first, and found no differences.

I looked at my code, and nothing jumped out as wrong.

When I looked closer I noticed that I'd been using DiffuseMaterial.Color instead of DiffuseMaterial.Brush

According to MSDN:

Brush: "Brush to be applied as a Material to a 3-D model."

Color: "The color allowed to emit from the Material. The default value is #FFFFFF. Since all colors make up white, all colors are visible by default."

Obviously? .Color was the wrong choice.

It seems I've got plenty to learn ...

Tuesday, January 30, 2007

Browsing old books

Doesn't sound like much fun does it? Well, consider that the books in questions were hand written by Leonardo da Vinci or that one of the books were Alice under ground or something other like that, then it seems much more interesting. The British library has done just that, published a few selected books in a WPF showcase application that feels close to reading a real book. See for yourself!

Prerequisites: .NET 3.0, Internet Explorer and a reasonably modern computer.

Technorati tags: ,

Tuesday, November 28, 2006

.NET 3.0 artikelserie fortsättning

Del 4 och 5 är skrivna på pellesoft och kommer att släppas inom kort. Denna gång handlar det om WPF.

Technorati tags: , ,

Tuesday, November 21, 2006

.NET 3.0 artikelserie på gång

Har börjat skriva en artikelserie om .NET 3.0 på pellesoft.

Del 1, 2, 3 redan klara och handlar främst om WCF. Nästa i tur är ytskrap av WPF. Del 1 släpptes idag. Del 2 kommer i morgon och del 3 i övermorgon.

Technorati tags: , ,

Tuesday, November 07, 2006

Monday, October 02, 2006

<Blink> on steroids...

One of the long-standing jokes at the office is about using the much hated <Blink> tag in XAML user interfaces. (The other one being about mapping the UI onto the inside of a sphere..)

Disclaimer: This is NOT in ANY WAY suggested for any use WHATSOEVER. We have already seen way too many blinking webpages, thank you very much. And the code is "proof-of-concept" quality..

So basically, I wanted to do a a component that I can use in XAML, making it's content blink. And just for the fun of it, I wanted the on and off durations tunable, and the same goes for the durations of the fade in and fade out between those two extremes.

So here goes nothing...

First a little test case:

<Window x:Class="Blink.Window1"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src
="clr-namespace:BlinkTest" Title="Blink" Height="300" Width="450">

<StackPanel Orientation="Vertical">
<!-- Use default blinking settings -->
<src:Blink>
<TextBlock Padding="20" Margin="20" Background="Pink">Hello World</TextBlock>
</src:Blink>
<!-- Explicitely specify blinking settings -->
<src:Blink OnDuration="1" OffDuration="1" FadeInDuration="0" FadeOutDuration="0">
<StackPanel Orientation="Horizontal" Margin="50">
<Label>Some nice text about the button</Label>
<Button>Click Me!</Button>
</StackPanel>
</src:Blink>
</StackPanel>
</Window>


And then the code for the Blink class


The using statements


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;

We are inheriting System.Windows.Controls.Control so we have to do some cruft related to layout




protected override int VisualChildrenCount {
get { return Child != null ? 1 : 0; }
}

protected override Visual GetVisualChild(int index) {
if (index > 0 || Child == null) throw new ArgumentException("index");
return Child;
}

protected override Size MeasureOverride(Size constraint) {
Size sizeDesired
= new Size(0, 0);
if (Child != null) {
Child.Measure(constraint);
}
sizeDesired.Width
+= Child.DesiredSize.Width;
sizeDesired.Height
+= Child.DesiredSize.Height;

return sizeDesired;
}

protected override Size ArrangeOverride(Size arrangeBounds) {
if (Child != null) {
Rect rect
= new Rect(
new Point((arrangeBounds.Width - Child.DesiredSize.Width) / 2,
(arrangeBounds.Height
- Child.DesiredSize.Height) / 2),
Child.DesiredSize);
Child.Arrange(rect);
}
return arrangeBounds;
}


And for the animation to work we need a number of dependency properties for the durations




Now, all that is left is just the blinking animation. An obvious choice for

// Using a DependencyProperty as the backing store for OffDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OffDurationProperty;

// Using a DependencyProperty as the backing store for OnDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OnDurationProperty;

// Using a DependencyProperty as the backing store for RiseDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FadeInDurationProperty;

// Using a DependencyProperty as the backing store for DeclineDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FadeOutDurationProperty;

public double OnDuration {
get { return (double)GetValue(OnDurationProperty); }
set { SetValue(OnDurationProperty, value); }
}

public double OffDuration {
get { return (double)GetValue(OffDurationProperty); }
set { SetValue(OffDurationProperty, value); }
}

public double FadeInDuration {
get { return (double)GetValue(FadeInDurationProperty); }
set { SetValue(FadeInDurationProperty, value); }
}

public double FadeOutDuration {
get { return (double)GetValue(FadeOutDurationProperty); }
set { SetValue(FadeOutDurationProperty, value); }
}

static Blink() {
// register dependency properties
OffDurationProperty = DependencyProperty.Register("OffDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(1.0));
OnDurationProperty
= DependencyProperty.Register("OnDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(1.0));
FadeInDurationProperty
= DependencyProperty.Register("FadeInDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(0.2));
FadeOutDurationProperty
= DependencyProperty.Register("FadeOutDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(0.5));
}
thing to animate is UIElement.OpacityProperty. So that's what I'm using, and to avoid a lot of fuzz with StoryBoards and stuff I set up the animation with the help of a DoubleAnimationUsingKeyFrames.




// setup the animation
DoubleAnimationUsingKeyFrames animation = new DoubleAnimationUsingKeyFrames();
animation.RepeatBehavior
= RepeatBehavior.Forever;

double currentSeconds = 0; // keep track of cumulated time
LinearDoubleKeyFrame start = new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(
0)));
animation.KeyFrames.Add(start);

currentSeconds
+= FadeInDuration;
LinearDoubleKeyFrame fadeInFrame
= new LinearDoubleKeyFrame(1,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(fadeInFrame);

currentSeconds
+= OnDuration;
LinearDoubleKeyFrame onFrame
= new LinearDoubleKeyFrame(1,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(onFrame);

currentSeconds
+= FadeOutDuration;
LinearDoubleKeyFrame fadeOutFrame
= new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(fadeOutFrame);

currentSeconds
+= OffDuration;
LinearDoubleKeyFrame offFrame
= new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(offFrame);

Child.BeginAnimation(UIElement.OpacityProperty, animation);



And here is the whole Blink.cs in its entirety once again:




using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;


namespace BlinkTest {
[ContentProperty(
"Child")]
public class Blink : Control {
UIElement m_child;

public UIElement Child {
get { return m_child; }
set {
if (m_child != null) {
RemoveVisualChild(m_child);
RemoveLogicalChild(m_child);
}
if ((m_child = value) != null) {
AddVisualChild(m_child);
AddLogicalChild(m_child);

// setup the animation
DoubleAnimationUsingKeyFrames animation = new DoubleAnimationUsingKeyFrames();
animation.RepeatBehavior
= RepeatBehavior.Forever;

double currentSeconds = 0; // keep track of cumulated time
LinearDoubleKeyFrame start = new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(
0)));
animation.KeyFrames.Add(start);

currentSeconds
+= FadeInDuration;
LinearDoubleKeyFrame fadeInFrame
= new LinearDoubleKeyFrame(1,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(fadeInFrame);

currentSeconds
+= OnDuration;
LinearDoubleKeyFrame onFrame
= new LinearDoubleKeyFrame(1,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(onFrame);

currentSeconds
+= FadeOutDuration;
LinearDoubleKeyFrame fadeOutFrame
= new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(fadeOutFrame);

currentSeconds
+= OffDuration;
LinearDoubleKeyFrame offFrame
= new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(offFrame);

Child.BeginAnimation(UIElement.OpacityProperty, animation);
}
}
}

protected override int VisualChildrenCount {
get { return Child != null ? 1 : 0; }
}

protected override Visual GetVisualChild(int index) {
if (index > 0 || Child == null) throw new ArgumentException("index");
return Child;
}

protected override Size MeasureOverride(Size constraint) {
Size sizeDesired
= new Size(0, 0);
if (Child != null) {
Child.Measure(constraint);
}
sizeDesired.Width
+= Child.DesiredSize.Width;
sizeDesired.Height
+= Child.DesiredSize.Height;

return sizeDesired;
}

protected override Size ArrangeOverride(Size arrangeBounds) {
if (Child != null) {
Rect rect
= new Rect(
new Point((arrangeBounds.Width - Child.DesiredSize.Width) / 2,
(arrangeBounds.Height
- Child.DesiredSize.Height) / 2),
Child.DesiredSize);
Child.Arrange(rect);
}
return arrangeBounds;
}

// Using a DependencyProperty as the backing store for OffDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OffDurationProperty;

// Using a DependencyProperty as the backing store for OnDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OnDurationProperty;

// Using a DependencyProperty as the backing store for RiseDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FadeInDurationProperty;

// Using a DependencyProperty as the backing store for DeclineDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FadeOutDurationProperty;

public double OnDuration {
get { return (double)GetValue(OnDurationProperty); }
set { SetValue(OnDurationProperty, value); }
}

public double OffDuration {
get { return (double)GetValue(OffDurationProperty); }
set { SetValue(OffDurationProperty, value); }
}

public double FadeInDuration {
get { return (double)GetValue(FadeInDurationProperty); }
set { SetValue(FadeInDurationProperty, value); }
}

public double FadeOutDuration {
get { return (double)GetValue(FadeOutDurationProperty); }
set { SetValue(FadeOutDurationProperty, value); }
}

static Blink() {
// register dependency properties
OffDurationProperty = DependencyProperty.Register("OffDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(1.0));
OnDurationProperty
= DependencyProperty.Register("OnDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(1.0));
FadeInDurationProperty
= DependencyProperty.Register("FadeInDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(0.2));
FadeOutDurationProperty
= DependencyProperty.Register("FadeOutDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(0.5));
}
}
}


The code was developed on Vista using the RC1 bits of the .NET 3.0 SDK



Technorati tags: ,

Sunday, September 10, 2006

Playing around in WPF land

With a little (ok, a lot of) help from Josh Smith and Karsten Januszewski/Andrew Whiddett I managed to put together a little ComicBook application that utilizes Asynchronous databinding, animated image transitions. It's not quite finished yet, but the parts that are left should mostly be a walk in the park (i.e. known territory). Not bad for an evenings worth of work, considering this is my first "real" WPF application.

The learning curve is steep...

I probably post more technical stuff when I get it closer to what I want it to be.