How to specify units in Dialog Units in WPF? - wpf

i'm trying to figure out how to layout a simple dialog in WPF using the proper dialog units (DLUs).
i've come up with a simpler example, that cuts straight to the problem:
You can check the Windows UX Guidelines to see where these measurements come from.
The short version is:
dlu = dialog unit
dlu is based on the font size (items change with user's font size)
a horizontal dlu is different from a vertical dlu (dlu's are not square)
This comes from the definition of a dialog unit: the average character is 8dlus high by 4dlus wide.
Georgia 14pt:
If you use a smaller font (i.e. 8pt Tahoma verses 14pt Georgia), the dlus get smaller:
Segoe UI 9pt:
Note: You'll notice that resolution (i.e. dpi) has no impact on the discussion.

You could try creating a Converter which returns the value multiplied by whatever your DLU is
For example, I created a MathConverter (code is here) which would let you specify something like
<Button Height="{Binding Source={x:Static local:Settings.VerticalDLU,
Converter={StaticResource MathConverter},
ConverterParameter=#VALUE*14}"
Width="{Binding Source={x:Static local:Settings.HorizontalDLU,
Converter={StaticResource MathConverter},
ConverterParameter=#VALUE*50}" />
To make it easier to write and read, you can create a class that inherits from Binding and setup a default binding properties so all you have to do is
<Button Height="{local:MyVDluBinding Source=14}"
Width="{local:MyHDluBinding Source=50}" />
I've never tried overwriting a binding with defaults for Converter and ConverterParameter, but I have overwritten a binding for validation purposes so I believe it's possible.
The ValidationBinding I created could be used like Text="{local:ValidationBinding MyProperty}" and automatically set ValidatesOnDataError=True, ValidatesOnException=True, UpdateSourceTarget=PropertyChanged in the bindings.

Related

Vertical alignment of Label inside TextBlock is wrong

In a WPF popup window that I display to the user I want to put a text that is bound to a variable inside a block of text.
I've tried to do so by putting a <Label> inside a <TextBlock>.
However, this leads to alignment problems, see markup and picture below.
I wonder how I can get the text in the Label to align vertically with the text in the TextBlock, or if there is a better solution?
I could put the entire text in a variable, but then I don't know how to get <Bold> formatting for part of the text.
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap"
VerticalAlignment="Top" Height="99" Width="339" Grid.RowSpan="3">
The user is <Bold>responsible</Bold> for
<Bold><Label Content="00" Padding="0"/></Bold> vehicles
</TextBlock>
You can use the Run inline element for data binding text.
Starting in the .NET Framework 4, the Text property of the Run object is a dependency property, which means that you can bind the Text property to a data source.
It also has properties for styling the text, like FontWeight, no need for <Bold> here.
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap"
VerticalAlignment="Top" Height="99" Width="339" Grid.RowSpan="3">
The user is <Bold>responsible</Bold> for
<Run Text="{Binding YourProperty}" FontWeight="Bold"/> vehicles
</TextBlock>
The result looks like this:
As a general note on the issue: The elements inside TextBlock should be inline elements. Here is an article that has links to other inline elements that could be useful to you, like span that allows for grouping other inline elements and applying styles or to them.
How to: Manipulate Flow Content Elements through the Inlines Property
WPF has two fundamental base types, UIElement and ContentElement. While a TextBlock and Label are UIElements, the content of TextBlock are inlines, which are ContentElements. That is why the alignment is off, they serve very different purposes. You can read up more on the differences and concepts here:
Base Element APIs in WPF Classes
UIElement
UIElement is a base class for WPF core level implementations building on Windows Presentation Foundation (WPF) elements and basic presentation characteristics.
ContentElement
Provides a WPF core-level base class for content elements. Content elements are designed for flow-style presentation, using an intuitive markup-oriented layout model and a deliberately simple object model.
Flow Document Overview

Create/Modify new WPF UI components

I want to change the graphical UI elements in WPF.
For example, I want to use a kind of a stack panel, but on the other hand I want to show my details in a star, or circle, etc.
Maybe setting a bitmap as a background, but I am working with lots of Data using zoom tool.
I found tutorials, documentation only for changing attributes of "old components", but nothing to make new ones.
Great resource for WPF beginners is www.wpftutorial.net
One of the best idea of WPF is separation of concerns:
UI Control = Logic in Code/XAML + Template
Using templates in XAML we can vary representation without modifying the control.
For example, if there is a need in creation of list of items. Then we can use ListBox control:
<ListBox>
<ListBoxItem>USA</ListBoxItem>
<ListBoxItem>UK</ListBoxItem>
</ListBox>
By default LisboxItem internal part is just binded TextBlock.
Now making UI modification without changing control source code:
<ListBox ImageSource="{Binding PathToSource}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ProjectIcon}"/>
<TextBlock Text="{Binding Path=PropertyName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
there appears image and text.
If there is a need in creating exclusive control then you can always use Custom Control.
Using raster images (e.g. PNG) is not good point, especially with zoom behaviour. If it is possible better to use vector images, that can be created in XAML or imported from SVG.

Is it possible to display a WPF Control in real-world, device-independent units? Milimeters, for example?

I am designing a product emulator in WPF. Our company sells a handheld electronic medical device, and we'd like to experiment with user interaction with a "virtual", skinned (sort of) GUI to represent buttons and leds.
Is there a way to "force" WPF to display stuff with its real-world dimension on the screen (by getting monitor dpi information, or something like this)?
As another example, suppose I can layout a plastic, standard-sized Credit Card using WPF controls (DockPanels, Labels, Canvases, etc.), could I consistently display it with the dimensions of a real credit card, across different desktops/laptops?
UPDATE:
I've found a clever way to design in "real milimeters" by using a ViewBox as the LayoutRoot of my window. Size of anything inside can be "designed in milimeters", and will be displayed in physical size PROVIDED that I can get the right pixel size for the ViewBox. And THAT remain a certain mistery:
The relation between pixels and milimeters in my desktop seems to be 3.5 (where does this number come from?
How could I implement a code-behind DataBound property or ValueConverter so that I could define a Zoom Level (via slider or hardcoded) to display a scaled (larger) version of the device if needed?
I "sort of" solved the problem with the following strategy:
My MainWindow has a ViweBox as LayoutRoot, and its Child is some container control (Border in this case) which represent the physical object;
The ViewBox and everything inside it is defined as if WPF units were milimeters. So, if the product has a physical Width of 68mm, the Width property of the container control is 78. Everything else that is nested inside is treated as if WPF units were actual milimeters;
The ViewBox has its Width and Height bounded to the Height and Width of the container control using a ValueConverter which multiplies by (96/25.4);
Since everything inside a viewbox is scaled to fit (ViewBox.Stretch=Uniform), the value converter just resizes the viewbox, and the rest of layout redimensioning is achieved by the ViewBox embedded rendering transform (that I assume to be efficient enough).
My XAML:
<Window.Resources>
<local:PixelsToMilimeters x:Key="SizeConverter"/>
</Window.Resources>
<Viewbox x:Name="LayoutRoot" Stretch="Uniform"
Width="{Binding Width, ElementName=Case, Converter={StaticResource SizeConverter}}"
Height="{Binding Height, ElementName=Case, Converter={StaticResource SizeConverter}}">
<Border x:Name="Case" Width="108" Height="68">
(...)
My code-behind:
(...)
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (double)value * (96/25.4);
}
(...)
Of course the "how can I be sure that DPI is actually 96?" is not solved, but that could be encapsulated, and the final result is close enough to the expected one, for my purposes.

WPF : Multiple views, one DataContext

I'm working on a WPF application which must handle multiple screens (two at this this time).
One view can be opened on several screens and user actions must be reflected consistently on all screens.
To achieve this, for a given type of view, a single DataContext is instantiated. Then, when a view is displayed on a screen, the unique DataContext is attached to it. So, one DataContext, several views (same type of view/xaml).
So far so good. It works quite well in most cases.
I do have a problem with a specific view which relies on ItemsControl. These ItemsControl are used to display UIElements dynamically build in the ViewModel/DataContext (C# code). These UIElements are mostly Path objects. Example :
<ItemsControl ItemsSource="{Binding WindVectors}">
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Canvas IsItemsHost="True" />
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
Here, WindVectors is a ObservableCollection<UIElement>.
When the view is opened the first time, everything is fine. The problem is that when the view is opened one another screen, all ItemsControl are removed from the first screen and displayed one the second screen. Other WPF components (TextBlock for instance) on this view react normally and are displayed on both screens.
Any help would be greatly appreciated.
Thanks.
Fabrice
This is the expected behavior (ie been that way since winforms)- this is because the ObservableCollection is a reference. This wont happen with value types, only reference types.
The short answer is 'dont do that'. You could try looking into defining a collection view in the xaml or code a custom data provider and bind to one of those instead.

WPF Binding Help

I haven't used WPF that much so the solution to this is probably pretty easy.
In the ide I'm developing it will have multiple controls(text editor) each being hosted in a tab, much like VS does for each source file. When the user clicks new the "host" creates a new EditorWindow(a usercontrol), creates a new tab, and tells the tab to display the EditorWindow it created, and then updates a property called currentWindow (of type EditorWindow) with the one that's currently active. Inside the EditorWindow is the text editor whose name is textEditor(also a property). What I'm trying to do is take this code from the quick start source of the text editor control I'm using
<StackPanel>
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsIndicatorMarginVisible}" Content="Indicator margin visible" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsLineNumberMarginVisible}" Content="Line number margin visible" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsRulerMarginVisible}" Content="Ruler margin visible (useful for fixed-width fonts only)" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsSelectionMarginVisible}" Content="Selection margin visible" />
</StackPanel>
put that in the host controls xaml, and bind the checkboxes to the syntax editor. I've tried a couple different things to no avail. Control is the name of the window hosting all the tabs, and path is obviously supposed to be the property that the checkboxes are bound too. I'm pretty sure the problem is that at initial run-time currentWindow isn't initialized so therefore my bindings aren't ever getting updated, but I'm at a loss as to how to fix this issue. Thanks!
Since you are new to WPF, you may not know that properties have to implement some sort of change notifications in order for bindings to work. For instance, if any of the properties in the the path "currentWindow.textEditor.IsIndicatorMarginVisible" change, you need to inform the binding engine that it has changed. If you implement these properties as DependencyPropertys, the change tracking comes for free. Otherwise, you should implement INotifyPropertyChanged.
I've found that the Snoop utility is the easiest way to do quick binding debugging, you should try using it and see if it tells you anything useful on the bound properties.

Resources