I have a WPF Window style defined thusly:
<Style
x:Key="Applet"
TargetType="{x:Type Window}">
<Setter
Property="WindowStyle"
Value="None" />
<Setter
Property="WindowState"
Value="Maximized" />
<Setter
Property="Title"
Value="Hindenburg" />
<Setter
Property="FontFamily"
Value="Arial" />
<Setter
Property="Height"
Value="650" />
<Setter
Property="Width"
Value="850" />
</Style>
My application then defines several screens using this style (FlowWindow is just derived from Window with a few extra bits):
<uControl:FlowWindow
x:Class="KaleidoscopeApplication.DisposablesScan"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:KaleidoscopeApplication"
xmlns:uControl="clr-namespace:KaleidoscopeApplication.Controls"
Style="{StaticResource Applet}"
Loaded="disposablesScanWindow_Loaded"
Unloaded="disposablesScanWindow_Unloaded">
<Canvas>
<!-- Top Bar Background -->
<Image
Source="Resources/Elements/Backgrounds/16.png" />
text etc etc...
</Canvas>
My question - How do I define a textblock that will be displayed on every Window that uses this style? For example, if I want a logo displayed in the upper right corner of every screen...
Since the style defines things like size and font and not the content of the canvas, I'm not sure how to go about this.
Thanks in advance!
EDIT: FlowWindow is not a UserControl. It is just part of my KaleidoscopeApplication.Controls namespace. It's defined as:
public class FlowWindow : Window
{
public FlowWindow()
: base()
{ }
/// <summary>
/// Transition smoothly to another FlowWindow.
/// </summary>
/// <param name="toWindow">The window to transtion to.</param>
public override void Transition(FlowWindow toWindow)
{
...
}
}
You can define custom Dependency Properties on your FlowWindow class, which can be set in the Style Setters. For instance, if you created a LogoImageProperty named "LogoImage", you could bind to it from the XAML like this:
<Canvas>
<!-- Top Bar Background -->
<Image
Source="{Binding LogoImage, RelativeSource={RelativeSource Mode=Self}}" />
text etc etc...
</Canvas>
This tells the FlowWindow to use itself as the binding context rather than the DataContext, but only for that particular binding.
UPDATE:
Since your FlowWindow is just a logical wrapper (and does not have any visual content), you might consider a few other possibilities:
Re-use a single custom Window class for all of your windows with your standard layout/style and place your current window contents inside UserControls. Now you can host the specific UserControl via a ContentPresenter and DataTemplate on the standard Window. This works especially well if you're following an MVVM pattern and can simply pass in the view model to be visualized by your Window.
You can create a new ControlTemplate for the Window with the layout you're after. See this answer for more details.
How about making a base class of the window where you can define the style for showing the logo and text box, the title using data binding. Then extend each other window in your application from the base window.
One possibilty is to add something in the constructor of your FlowWindow. It's difficult to give a full example here because I don't know your exact design, but here is some pseudo-code:
public FlowWindow()
: base()
{
Image logo = new Image();
ImageSourceConverter converter = new ImageSourceConverter();
string path = "pack://application:,,,/Resources/logo.png";
ImageSource source = (ImageSource)converter.ConvertFromString(path);
logo.Source = source;
logo.Width = 50d;
// Add properties and attached properties like Canvas.LeftProperty,
// Canvas.TopProperty, Canvas.ZIndexProperty, etc., and then
// find the first child of the FlowWindow, and add the image
// to the Children collection of that first child
}
Related
I like the way Prism 5.0 Interactivity works, but it can just open UserControls and Panels and such inside a Window it creates. So since it cannot place a Window inside another Window you can't pass a view for it that has Window as the root element.
The problem is, when it places my UserControl inside the Window it created, the Window has not a MinWidth or MinHeight or a ResizeMode="NoResize" option to be selected, thus, the user interface becomes horrible.
Are there any ways to control the Window's properties so I can customize it as I want?
PS: It amazes me how a big and important company as Microsoft can release a Best Practices library with stuff missing like that.
As requested, here comes a code example:
In order to open a view in a new Window in Prism, you have to add this to the current view (the view that's going to invoke the creation of a new Window in it's ViewModel):
<prism:InteractionRequestTrigger SourceObject="{Binding ItemSelectionRequest, Mode=OneWay}">
<!-- This PopupWindowAction has a custom view defined. When this action is executed the view will be shown inside a new window -->
<!-- Take into account that the view and its view model are created only once and will be reused each time the action is executed -->
<prism:PopupWindowAction>
<prism:PopupWindowAction.WindowContent>
<views:ItemSelectionView />
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
If you change this ItemSelectionView from a UserControl to a Window, you will get this exception:
Since basically Prism will try to place a Window inside a Window when it creates a new Window and a new ItemSelectionView and tries to put one inside the other...and Windows are suppose to be the root always, but in this case Window ItemSelectionView will be placed as a child of a new Window.
More information about how this works, please go to the link I posted.
For now I am using code behind to tweak the window, I check if this UserControl is the root of the Window, and only in that case I teak the Window's settings (this isn't ideal, but still isn't a violation of MVVM):
private void OnLoaded(object sender, System.Windows.RoutedEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
if (parentWindow != null && parentWindow.Content == this)
{
parentWindow.ResizeMode = ResizeMode.NoResize;
parentWindow.SizeToContent = SizeToContent.Height;
parentWindow.MinHeight = this.MinHeight;
parentWindow.MinWidth = this.MinWidth;
}
}
There is a pretty simple solution.
Since the PopupWindowAction creates the wrapper windows as instances of the Window class, you can apply a default style to the type Window in your app.xaml.
<Application.Resources>
<Style TargetType="{x:Type Rectangle}"/>
<Style TargetType="{x:Type Window}">
<Setter Property="Width" Value="500"/>
<Setter Property="ResizeMode" Value="NoResize"/>
<Setter Property="ShowInTaskbar" Value="False"/>
</Style>
</Application.Resources>
I have a wpf application in which on clicking a button "Submit" a DXMessage Box appears with a summary text outlining the various operations made by the user. The text to be displayed was too large and the default textbox width and height of the DXMessageBox was not sufficient to display the text in a readable format to the user. In order to overcome this I wrote my own control template for the DXMessageBox in my app.xaml file:
<Application.Resources>
<ResourceDictionary>
<ControlTemplate TargetType="{x:Type dx:DXMessageBox}" x:Key="DXMessageBoxTemplate">
<TextBox Height=500 Width=500 Text="{Binding Path=Text, RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
<Style TargetType="{x:Type dx:DXMessageBox}">
<Setter Property="Template" Value="{StaticResource DXMessageBoxTemplate}"/>
</Style>
</ResourceDictionary>
</Application.Resources>
Now this works fine and the text is automatically bound to the variable in my view model class as follows:
string result;
DXMessageBox.Show(result);
The variable "result" is data that I am deriving from a datatable in myview model class and converting to string.
However my application requires the entire gridcontrol to be displayed in the DXMessageBox, so I tried the above same approach but I get stuck at two places:
1)In the controlTemplate defined in the app.xaml file where I shall add my gridcontrol , what should the itemsSource of the xaml be such that my gridcontrol is bound to the datatable declared in my viewmodel class.
2)When I call the DXMessageBox.Show method after adding the grid control, what arguments to pass to that method.
It is better to use the DXDialog instead of DXMessageBox to accomplish your task, because the DXMessageBox is not intended to be used for displaying custom content like DXGrid.
When working with the DXDialog, just create a separate UserControl that contains your DXGrid and implements all related presentation logic and pass this UserControl into DXDialog.Content property:
void ShowDialog_Click_1(object sender, RoutedEventArgs e) {
DXDialog dlg = new DXDialog("Information", DialogButtons.Ok, true);
dlg.Content = new UserControlWithDXGrid();
dlg.SizeToContent = System.Windows.SizeToContent.WidthAndHeight;
dlg.Owner = this;
dlg.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
dlg.ShowDialog();
}
P.S. I see you are tagged this question with MVVM tag. Thus I believe in this case you should use the DialogService to accomplish this task in MVVM-way.
Please start from reading documentation that clearly describe all needed steps in this regard.
I have an app where I set a theme (ResourceDictionary) on the main Window. Then all children "inherits" styles, themes etc. via the DynamicResourceExtension. This works.
However, i have a custom Popup control to handle long press animation on buttons:
class LongPressAnimationControl : Popup
{
static LongPressAnimationControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LongPressAnimationControl),
new FrameworkPropertyMetadata(typeof(LongPressAnimationControl)));
}
/* Various DP's, events, properties, methods ETC. */
}
with a default style in Generic.xaml:
<Style TargetType="{x:Type controls:LongPressAnimationControl}">
<Setter Property="Delay" Value="00:00:00.500" />
<Setter Property="Duration" Value="00:00:01.000" />
<Setter Property="Image" Value="{StaticResource Image.LongPress}" />
<Setter Property="EasingFunction"><Setter.Value><CubicEase/></Setter.Value></Setter>
</Style>
This works. the properties change to the ones set in the style.
However, i would like the popup to use a style defined in the Theme.xaml so the properties etc. can be themed.
If i move the style to my Theme.xaml, then the style is not picked up (properties does not change).
I've also tried to add a key to the Style, and a resource reference in code, like so:
public LongPressAnimationControl()
{
SetResourceReference(StyleProperty, "LongPressAnimationControlStyle");
//....
}
Anyone with an idea of how to solve this?
You should include Theme.xaml in App.xaml resource, so WPF can find the resource.
I have set up a complete application using C# .NET 4, Prism and Unity that implements the INavigationAware interface on the ViewModel of an MVVM pattern. My window (Shell.xaml) is very simple at the moment (static string for RegionName to avoid magic strings):
Shell.xaml
<Grid>
<ContentControl prism:RegionManager.RegionName="{x:Static Infrastructure:RegionNames.ContentRegion}" />
</Grid>
Each of my views contains buttons that allow the user to open another view using a centralized CompositeCommand to which I attach a DelegateCommand in the Shell like so:
ViewA.xaml
<Button Name="AcceptButton" Content="Accept"
Command="{x:Static Infrastructure:ApplicationCommands.NavigateCommand}"
CommandParameter="{x:Type Concrete:ViewB}"
ApplicationCommands.cs
public static class ApplicationCommands {
public static CompositeCommand NavigateCommand = new CompositeCommand();
}
ShellViewModel.cs
public ShellViewModel(IRegionManager regionManager) {
_regionManager = regionManager;
NavigateCommand = new DelegateCommand<object>(Navigate, CanNavigate);
ApplicationCommands.NavigateCommand.RegisterCommand(NavigateCommand);
}
private void Navigate(object navigatePath) {
if (navigatePath != null) {
_regionManager.RequestNavigate(RegionNames.ContentRegion, navigatePath.ToString(), NavigationCallback);
}
}
I have several more views tied in and the navigation is working great. Now comes the changes that are failing. Having random buttons on each screen is really ineffective and contrary to good design so I am trying to pull the buttons out for a centralized toolbar. I have pulled the ViewA.xaml button code out of the ViewA.xaml file ( which contains much more content but not shown for overkill reasons ) and put it into a ViewAButonn.xaml file. I then modified the Shell.xaml and add a second region:
Modified Shell.xaml
<Grid>
<ContentControl prism:RegionManager.RegionName="{x:Static Infrastructure:RegionNames.ContentRegion}" />
<ContentControl prism:RegionManager.RegionName="{x:Static Infrastructure:RegionNames.NavRegion}" />
</Grid>
I can add my new ViewAButton.xaml to the region without any issue and when I click it the View contents are then placed properly into the ContentRegion.
My issue arises here though. My first screen is a TOS agreement screen that cannot display the toolbar until "Accept" button is clicked. I am terrified at the thought of handling this in the ViewModel as I have it properly decoupled right now. Do I modify the View to contain a property that can be read during navigation to hide the region? If so where in the navigation process can I get access to the View that is activated by Unity? All of my views implement an IView interface that just exposes an IVewModel as per the MSDN instruction on setting up a proper prism MVVM. How can I hide this new toolbar on the TOS acceptance screen?
Based on my understanding it would not be possible to collapse a Region. The View would get Hidden but the space where the Region is located would remain empty.
If you want the TOS screen to fill the entire window, you could create two levels of Regions:
For example, you could declare a "WindowRegion" Region in the Shell View which fills the complete Window and where the TOS screen and a defined "CustomRegionsView" would get registered.
Then, the CustomRegionsView would define both Regions you mentioned above so the App can navigate between the TOS screen and any View with the two regions you described.
Therefore, when "Accept" button is clicked from the TOS screen, Navigation to the CustomRegionsView should be performed as also to the particular Views which are registered on the ContentRegion and NavRegion.
I hope you find this helpful.
It’s worth having a closer look at the “Prism for WPF Reference Implementation”, in particular, Shell.xaml in Line 173 and onwards. This’ll show you that it’s indeed possible to collapse a Prism region.
<ContentControl x:Name="ActionContent" prism:RegionManager.RegionName="{x:Static inf:RegionNames.ActionRegion}">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Grid>
<Controls:RoundedBox />
<ContentPresenter Margin="10,0,10,0" Content="{TemplateBinding Content}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="false">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
They combine this with a data converter that is being made use of within the row definitions of a grid layout:
<Grid.Resources>
<StockTraderRI:VisibilityToStarHeightConverter x:Key="VisibilityToStarHeightConverter" />
</Grid.Resources>
<Grid.Rows>
…
<RowDefinition Height="{Binding Visibility, ElementName=ActionContent, Converter={StaticResource VisibilityToStarHeightConverter}, ConverterParameter=5}" />
…
where the converter itself is defined as
public class VisibilityToStarHeightConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if ((Visibility)value == Visibility.Collapsed) {
return new GridLength(0, GridUnitType.Star);
} else {
if (parameter == null) {
throw new ArgumentNullException("parameter");
}
return new GridLength(double.Parse(parameter.ToString(), culture), GridUnitType.Star);
}
}
…
I’ve omitted a couple of things here, but the picture is clear: you don’t actually need to make use of the zindex or other workarounds.
Again, as mentioned above, the implementation I pasted here is not mine, but part of the Prism Quick Starts over at Microsoft.
Edit (as commented: XY-Problem) - Problem:
I want to create my own control which has predefined styles and positions for special elements (Button,...), but in general everything should be able to be placed inside my custom control. The custom control in my case is just a "menubar" which should be able to be used anywhere in the "GUI code" - but there is no need it has to be there. But when it is used it should be the same style and behavior everywhere. A style is - I think - not enough, because there are also predefined elements in this menubar (e.g. Help is already in menubar)
Edit end.
I want to build a custom control (just a special stackpanel) in WPF with the following requirements:
can be used as any other control within a xaml
has defined styles for controls within the custom control
First I simply tried to create a UserControl containing a stackpanel with defined styles (in the xaml) for containing elements (e.g. Button). This UserControl contained the
<ContentPresenter />
in the xaml. With this method it is not possible to name the containing elements. E.g.:
<mynamespace:MyStackPanel>
<Button Name="w00t">This does not work!</Button>
</mynamespace:MyStackPanel>
Next try was to create a "real" custom control. This custom control is just a class without the xaml. Code is very simple. Class inherits from UserControl and just contains:
StackPanel sp = new StackPanel();
sp.Children.Add(new ContentPresenter());
this.AddChild(sp);
Woooohoooo, now it's possible to name the containing elements. But still a big problem: How to define the styles?
I could define the style for my very own custom control in a ResourceDictionary. But i have to add the ResourceDictionary to the global (App.xaml) Resources. And then I can define styles only for my custom control - not for the containing elements? - But anyway... doing it like this just feels wrong!
So the main question is: WHAT is the "correct" way of creating a custom control which can be used in xaml like any other control? If the second way is the correct way - how is it possible to set the style like I do it in a xaml (e.g. every Button in this element has a special style) and has it to be a "global" ResourceDictionary?
How is it implemented in third-party stuff?
Ok I made an example for you, which involves Custom Controls (as Opposed to UserControls)
Step 1:
Create a new class (code only, no XAML) derived from ContentControl (or whatever UI element that has a behavior similar to what you need)
public class ReusableContainer : ContentControl
{
public static readonly DependencyProperty ButtonProperty = DependencyProperty.Register("Button", typeof(Button), typeof(ReusableContainer), new PropertyMetadata(default(Button)));
public Button Button
{
get { return (Button)GetValue(ButtonProperty); }
set { SetValue(ButtonProperty, value); }
}
}
See how I'm defining the Button property as a DependencyProperty here. You can add more DPs for whatever "content placeholders" that you need in your custom control.
Step 2:
Have your predefined Styles for the UI elements inside the container in a separate ResourceDictionary:
CustomStyles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Background" Value="Green"/>
</Style>
</ResourceDictionary>
Step 3: in app.xaml, define an application-wide style for the ReusableContainer, which defines it's template:
<Application x:Class="WpfApplication14.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="{x:Type local:ReusableContainer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ReusableContainer}">
<ControlTemplate.Resources>
<ResourceDictionary Source="CustomStyles.xaml"/>
</ControlTemplate.Resources>
<ContentPresenter Content="{TemplateBinding Button}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
See how I'm using the TemplateBinding expression to define that the ContentPresenter's content is going to be defined by the Button property in the ReusableContainer.
Also notice how I'm Adding the Resources in CustomStyles.xaml to the ControlTemplate.Resources collection. This makes these resources available to all UI elements inside the Template.
Step 4:
Place your ReusableContainer in a Window:
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button1" Content="Hello! Button 1"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button2" Content="Hello! Button 2"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button3" Content="Hello! Button 3"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
</StackPanel>
</Window>