Use a string as Window Height in XAML - wpf

I made a window with some parameters:
<Window x:Class="MsgBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MsgBox" Height="300" Width="500" Topmost="True" WindowStartupLocation="CenterScreen" WindowStyle="None" Loaded="MsgBox_Loaded">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
</Window>
I want to change the height and width to these calculated strings. It gets the users screenwidth and height and divide it by four.
Public ReadOnly Property PrimaryScreenWidth As Double
Get
Return System.Windows.SystemParameters.PrimaryScreenWidth
End Get
End Property
Public ReadOnly Property PrimaryScreenHeight As Double
Get
Return System.Windows.SystemParameters.PrimaryScreenHeight
End Get
End Property
Private MsgBoxWidth As String = PrimaryScreenWidth \ 4
Private MsgBoxHeight As String = PrimaryScreenHeight \ 4
How to set it to my window?
Height="{x:static MsgBoxHeight }" Width="{x:static MsgBoxWidth }" ??

If you want to do this, why don't you simply
Me.Height = MsgBoxHeight
Me.Width = MsgBoxWidth
when you calculated the properties?

The syntax you're showing and presumably want, with the curly braces:
Height="{x:static MsgBoxHeight }" Width="{x:static MsgBoxWidth }"
is called Markup Extension syntax, it allows using a markup extension class to set a property value. Here's how you'd do it. First step, create the markup extenssion class:
Public Class MsgBoxHeight
Inherits System.Windows.Markup.MarkupExtension
Public Sub New()
End Sub
Public Overrides Function ProvideValue(serviceProvider As IServiceProvider) As Object
Return System.Windows.SystemParameters.PrimaryScreenHeight / 4
End Function
End Class
Next you'll add a xmlns:local="clr-namespace=YourNamespace" to the <Windows>, then you'll be able to use it like this: Height="{local:MsgBoxHeight}".
The complete XAML for the Window would look like this:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication6"
Title="MainWindow" Height="{local:MsgBoxHeight}" Width="525">
<Grid>
</Grid>
</Window>
Note
Using the Markup Extension means you're doing it from code. You are introducing a nice application-specific extension to XAML, but you need code for it to work. If you only need to do this for one Window it makes more sense to simply set the Height and Width of the window from the code-behind and not bother with the Markup Extension.

Related

Using named in reusable WPF control

I recentrly discovered "reusable controls" in WPF and I have a project where they seem to provide me with a solution to a problem I have.
Let me sketch the situation:
I need to make several UI elements. All of them share a common base, a common style/layout/template let's say, and they also share some common logic.
Next to that, all of these elements have some element-specific stuff.
You could say that I have some kind of inheritance here, but then for both XAML and CS.
The way I wanted to solve this, was by making an outer reusable element, I made a small example. The common part Is the Title label and the border. The element-specific UI can then be inserted into UserContent.
The code looks something like this (although it's simplified for the sake of brevity and conciseness, I also have an eventhandler and a routed event in my actual application):
ReusableControl.xaml
<UserControl x:Class="StackOverflowQuestion4.ReusableControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<Border BorderBrush="Black"
BorderThickness="1"
Width="400"
Height="200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Content="{Binding Title, ElementName=root}"
Grid.Row="0"/>
<ContentControl Content="{Binding UserContent, ElementName=root}"
Grid.Row="1"/>
</Grid>
</Border>
</UserControl>
ReusableControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace StackOverflowQuestion4
{
public partial class ReusableControl : UserControl
{
public ReusableControl()
{
InitializeComponent();
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(ReusableControl), new PropertyMetadata(string.Empty));
public object UserContent
{
get { return GetValue(UserContentProperty); }
set { SetValue(UserContentProperty, value); }
}
public static readonly DependencyProperty UserContentProperty =
DependencyProperty.Register("UserContent", typeof(object), typeof(ReusableControl), new PropertyMetadata(string.Empty));
}
}
Lovely, I can now use my special control in other parts of my code, and I can insert whatever I want into the UserContent field.
MainWindow.xaml
<Window x:Class="StackOverflowQuestion4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StackOverflowQuestion4"
mc:Ignorable="d"
Title="MainWindow"
SizeToContent="WidthAndHeight">
<Grid Width="800"
Height="600">
<local:ReusableControl Title="Test">
<local:ReusableControl.UserContent>
<Rectangle Width="300"
Height="100"
Fill="Blue"/>
</local:ReusableControl.UserContent>
</local:ReusableControl>
</Grid>
</Window>
This works great, but the problem arises when I start to name things. Simply adding a name to an element inside of my ReusableControl causes a compilation error.
<Rectangle Width="300"
Height="100"
Fill="Blue"
Name="LolWhatAmIDoing"/>
I get the following error:
MC3093 - Cannot set Name attribute value 'LolWhatAmIDoing' on element 'Rectangle'. 'Rectangle' is under the scope of element 'ReusableControl', which already had a name registered when it was defined in another scope.
This seems like such a small issue, but I cannot find an easy solution to this problem.
I found this thread on the forum, but it does not really provide a solution.
Since I'm pretty new to all of this, I also don't really get what the issue is, so apologies if I'm slow minded.
Should I move to CustomControls?
What you show is a simple property assignment: you set the value of type Rectangle to the property ReusableControl.UserContent. It's important to understand that the Rectangle is not part of the visual tree at this point. It's a simple property value that is only accessible via the property and not via the visual tree.
This all happens in the scope of MainWindow.
But the Rectangle is not a member of this scope. The ReusableControl is adding it to its own visual subtree or scope by binding the value of ReusableControl.UserContent to a ContentControl. This is were the Rectangle exists i.e. is rendered in the visual tree.
It effectively doesn't exist in the scope of MainWindow. It effectively only exists inside the ReusableControl in the "shape" of a ContentControl. This means that the scope of ReusableControl is the only name scope where you can register a name for child elements. It's also the only scope where you can directly reference it (if it had been defined and registered in this scope).
If you understand this, then you understand that the Rectangle is currently trying to register a name in the wrong scope, a scope in which it doesn't exist.
As a consequence, you cannot directly refer to it in the scope of MainWindow. You would have to dig into the ContentTemplate of the UserControl (which is a ContentControl) in order to get the nested ContentControl that actually hosts the Rectangle.

UserControl's DataContext: set it to viewmodel directly

I have a wpf usercontrol and a correspondent viewmodel (ChamberVm) made for it.
In viewmodel there is a property named 'UnitStatus'
But I got binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'UnitStatus' property not found on 'object' ''String' (HashCode=-1814504727)'. BindingExpression:Path=UnitStatus; DataItem='String' (HashCode=-1814504727); target element is 'VacGram' (Name='sysdgm'); target property is 'UnitStatus' (type 'KeyValuePair`2')
I have noted the mistake might be about DataContext setting in the header part of my control:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dgm="clr-namespace:VacSym;assembly=VacSymDgm"
xmlns:v="clr-namespace:VacViews"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="400"
DataContext="ChamberVm">
<Grid Name="gridMain">
<Grid.RowDefinitions>
<RowDefinition Height="0*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="15"></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="1">
<DockPanel x:Name="pnlDgm" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<dgm:VacGram x:Name="sysdgm" UnitStatus="{Binding UnitStatus}" DiagramFile="{Binding DiagramFile}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</DockPanel>
. . .
I want to know:
1, why this is not correct (?)
DataContext="ChamberVm"
2, what the 'String' means in the error message:
...not found on 'object' ''String'
Ting
The assignment
DataContext="ChamberVm"
assigns the string "ChamberVm" to the DataContext property.
In order to assign an instance of the ChamberVm class, you would have to write the following, with an appropriate namespace prefix:
<UserControl ...>
<UserControl.DataContext>
<v:ChamberVm/>
</UserControl.DataContext>
...
</UserControl>
In general, you should avoid to explicitly assign the DataContext property at all.
You would instead put the UserControl in a DataTemplate that is applied to e.g. a ContentControl that has a ChamberVm object assigned to its Content property. The DataContext of the UserControl would then be set automatically.
Your DataContext is a string "ChamberVm", not an object.
That's why you can't find UnitStatus.
<!-- Just set string in DataContext -->
<Window DataContext="ChamberVm">
</Window>
ChamberVm is string right.
So, of course, there is no UnitStatus (object) in DataContext (string).
And DataContext property is object type.
// DependencyProperty
public object DataContext { get; set; }
// You can put anything in.
It's a mistake that everyone makes often.
Anyway, you use DataContext in two main ways.
(To your liking)
1. DataContext Direct Binding in XAML.
<Window xmlns:vm="clr-namespace:VacViews.ViewModels">
<Window.DataContext>
<vm:ChamberVm/>
</Window.DataContext>
</Window>
You must declare the namespace of your view model at the top first. xmlns:vm="your namespace"
And open DataContext to declare the view model you created. This is to create an instance and assign it to DataContext.
2. Set Create Instance in Behind.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Your ViewModel set in DataContext.
DataContext = new ChamberVm();
}
}
Creating a 'view model' instance within the constructor block is the most impressive position.
Feel free to let me know if you have any further questions.

Partial declarations, must not specify different base classes?

When I changed this:
public partial class FrmCategories : UserControl
to this:
public partial class FrmCategories : MyUserControl
Where MyUserControl inherits from UserControl.
I got this error:
Error 2 Partial declarations of 'WpfTest.FrmCategories' must not
specify different base classes
\Projects\WpfTest\WpfTest\FrmCategories.xaml.cs 21 26 WpfTest
XAML:
<UserControl x:Class="WpfTest.FrmCategories"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:we="clr-namespace:WpfTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Background="Azure" Height="131" Width="229">
<StackPanel Margin="5,24,5,0" Name="catFrm" Height="75" VerticalAlignment="Top">
I'm beginning WPF (as the name of the project implies), so I expect a trivial error here
You need to change the root element of the XAML file:
<we:MyUserControl x:Class="WpfTest.FrmCategories"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:we="clr-namespace:WpfTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Background="Azure" Height="131" Width="229">
<StackPanel Margin="5,24,5,0" Name="catFrm" Height="75" VerticalAlignment="Top">
</we:MyUserControl>
partial only means that you can split the class definition into different files. But still all the partial definitions define only one class which as such may only have one base class.
If you want to inherit from MyUserControl, you have to change the xaml code, too:
<we:MyUserControl x:Class="WpfTest.FrmCategories"
...>
...
</we:MyUserControl>
Also, notice that you only have to specify the base class once, i.e. you could even change the C# code to
public partial class FrmCategories
because the base class has been defined in the xaml.

WPF Caliburn Micro: Exchanging UserControls in a Window dynamically using ContentControl

This question is related to Add a usercontrol to caliburm micro dynamically.
I have read any other related threads before open this new thread, but I still don't understand and find no solution. Please accept my apology if some of you take this as duplicate.
I have a window (MainView) contains "main" Grid (aka LayoutRoot) with 2 columns.
On left column there are 2 buttons: "Display View 1" and "Display View 2".
If user click "Display View 1", the "Display1View" (is a UserControl contains TextBlock with Text "View 1") should be shown on the right column, replace the current one.
If user click "Display View 2", the "Display2View" (is a UserControl contains TextBlock with Text "View 2") should be shown on the right column, replace the current one.
My sample code contains following views and viewmodels:
MainView.xaml and MainViewModel.cs
Display1View.xaml and Display1ViewModel.cs
Display2View.xaml and Display2ViewModel.cs
In my sample code the ContentControl doesn't recognize the UserControl. What am I doing wrong? How to bind ContentControl correctly? Please feel free to modify my sample code. Thank you in advance
MainView.xaml
<Window x:Class="TestCaliMiContentControl.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main View"
Width="525"
Height="350">
<Grid x:Name="LayoutRoot" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="100*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="LeftNavPanel" Grid.Column="0">
<Button x:Name="Display1" Content="Display View 1" />
<Button x:Name="Display2" Content="Display View 2" />
</StackPanel>
<ContentControl x:Name="MainGridContent" Grid.Column="1" />
</Grid>
</Window>
MainViewModel.cs
public class MainViewModel : PropertyChangedBase
{
private ContentControl _mainGridContent;
public ContentControl MainGridContent
{
get { return _mainGridContent; }
set
{
_mainGridContent = value;
NotifyOfPropertyChange(() => MainGridContent);
}
}
public void Display1()
{
//MainGridContent = new Display1ViewModel(); // cannot convert source type error
}
public void Display2()
{
// MainGridContent = new Display2ViewModel(); // cannot convert source type error
}
}
Display1View.xaml
<UserControl x:Class="TestCaliMiContentControl.Display1View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<TextBlock HorizontalAlignment="Center" FontSize="72"
Text="View 1"/>
</Grid>
</UserControl>
Display1ViewModel.cs
using System;
using System.Windows.Controls;
using Caliburn.Micro;
namespace TestCaliMiContentControl
{
public class Display1ViewModel : PropertyChangedBase {}
}
First, I would start by recommending you read the Caliburn.Micro documentation, specifically the part about Screens, Conductors, and Composition: http://caliburnmicro.com/documentation/composition
That being said, we can modify your code to get it working.
1) Since your MainViewModel is supposed to be conducting other items, it should descend from Conductor<T>. In this case, we will have it conduct the Caliburn Screen class.
public class MainViewModel : Conductor<Screen>
2) In MVVM, you view models should know nothing of your view. You should not see UI classes such as ContentControl. We could change your property to be of type Screen, but we actually don't need that property at all since we are using a conductor. So, remove the MainGridContent property and backing field.
3) Within your Display1 and Display2 methods, invoke Caliburn's conductor method ActivateItem to show the appropriate item.
public void Display1()
{
ActivateItem(new Display1ViewModel());
}
4) In your MainView.xaml you will need to bind your ContentControl to the conductor's active item property, which is, by convention, ActiveItem.
<ContentControl x:Name="ActiveItem" Grid.Column="1" />
5) Finally, since your conductor is conducting Screens, you need to make them screens. Screens are helpful because they have lifecycle and allow you to know when they are activated/deactivated. Do this for both Display1 and Display2.
public class Display1ViewModel : Screen {}
This should get you up and running.

Setting content of TextBlock and text of HyperlinkButton in silverlight custom control

I am trying to create a custom control that will display a hyperlink button with some text below the link. The idea is to have urgent messages show up on a screen of a Silverlight page. From what I have read, I thought that I should be able to create a new control and then create some dependancy properties and bind the dynamic parts of the component pieces to them in order to allow me to add multiple instances of the custom control to my Silverlight project. Here is my XAML that defines the control
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="WhatsNew.UrgentStoryGridControl"
d:DesignWidth="608" d:DesignHeight="65" Background="White">
<UserControl.Resources>
<Style x:Key="WhatsNewTitleStyle" TargetType="HyperlinkButton">
Removed for Brevity
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Height="65" Margin="0" VerticalAlignment="Bottom" Background="White">
<StackPanel>
<HyperlinkButton Style="{StaticResource WhatsNewTitleStyle}" Content="{Binding linkText}" HorizontalAlignment="Left" VerticalAlignment="Top" NavigateUri="{Binding linkURI}" Foreground="Red"/>
<TextBlock Style="{StaticResource WhatsNewTextStyle}" Text="{Binding storyText}" Margin="0,13,0,0" d:LayoutOverrides="Height"/>
</StackPanel>
</Grid>
In the code behind, I have created three dependancy properties
Partial Public Class UrgentStoryGridControl
Inherits UserControl
Public Shared linkTextProperty As DependencyProperty = DependencyProperty.Register("linkText", GetType(String), GetType(UrgentStoryGridControl), New PropertyMetadata("Link Text"))
Public Shared linkURIProperty As DependencyProperty = DependencyProperty.Register("linkURI", GetType(String), GetType(UrgentStoryGridControl), New PropertyMetadata("link.html"))
Public Shared storyTextProperty As DependencyProperty = DependencyProperty.Register("storyText", GetType(String), GetType(UrgentStoryGridControl), New PropertyMetadata("Story Text"))
Public Property linkText() As String
Get
Return GetValue(linkTextProperty)
End Get
Set(ByVal value As String)
SetValue(linkTextProperty, value)
End Set
End Property
Public Property linkURI() As String
Get
Return GetValue(linkURIProperty)
End Get
Set(ByVal value As String)
SetValue(linkURIProperty, value)
End Set
End Property
Public Property storyText As String
Get
Return GetValue(storyTextProperty)
End Get
Set(ByVal value As String)
SetValue(storyTextProperty, value)
End Set
End Property
End Class
When I place this control on my Silverlight project using Expression Blend, I see the three properties listed in the Miscellaneous section of the properties window as I would expect. The values from the PropertyMetadata are populated as the default values for these properties. Here is the code from my Silverlight project where I leave the default values alone:
<local:UrgentStoryGridControl x:Name="urgentStory" Height="65" />
Here is the code where I try to set the values to something:
<local:UrgentStoryGridControl x:Name="urgentStory" Height="65" linkText="Test Link Text" linkURI="testpage.html" storyText="Sample Story Text" />
Either way I attempt to use the control, I'm not getting anything displayed when I launch the application. I figure that I'm missing something small but after having spent a lot of time today researching this, I'm not finding anything that would indicate what I'm missing or doing wrong.
You need to set the DataContext in your custom UserControl or else your bindings won't work.
In your UrgentStoryGridControl's constructor, you should be able to set Me.DataContext = Me

Resources