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.
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'm working with Fluent.Ribbon, and I am desperately trying to vertically center the text of second level menu items. I actually wouldn't mind making second level have the same style as first level items if that would be easier.
From digging through the source code, I think I've found that the separate style is defined by the ControlTemplate ApplicationMenuSecondLevelItemTemplate. However, since I'm working on learning WPF, I'm not sure how to override that with styles.
I've tried simpler solutions, like this one which just makes the text disappear.
I've also tried all of the option in this post. However, none of those work either, and when I fix the last one to be the following to get rid of errors, the application just crashes.
<Style x:Key="CenteredTextMenuItem" TargetType="{x:Type MenuItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding}" HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center" FontSize="16" FontWeight="Bold"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Height" Value="30"/>
<Setter Property="Width" Value="188"/>
</Style>
How can I center a second level MenuItem's text with the Fluent.Ribbon control?
Thanks.
Edit:
Here is an image showing how the second level is not centered.
And nothing complicated with the code:
<Fluent:MenuItem Header="Print Invoice" Icon="NavIcons\Print_32.png" />
Here are possible solutions for your problem.
Simple, but somewhat ugly.
Create an event handler in code behind for the Loaded event of your menu items.
<Fluent:MenuItem Loaded="MenuItem_Loaded"/>
In this event handler, you can find the control template parts manually and override their appearance:
private void MenuItem_Loaded(object sender, RoutedEventArgs e)
{
Fluent.MenuItem menuItem = sender as Fluent.MenuItem;
if (menuItem != null)
{
TextBlock textBlock = menuItem.Template.FindName("textBlockDesc", menuItem) as TextBlock;
if (textBlock != null)
{
textBlock.Visibility = System.Windows.Visibility.Collapsed;
}
textBlock = menuItem.Template.FindName("textBlock", menuItem) as TextBlock;
if (textBlock != null)
{
textBlock.VerticalAlignment = System.Windows.VerticalAlignment.Center;
}
}
}
This is a bad solution, don't do it like that. I've just shown it so you can get an idea how could you access the template parts if you wish to. These strings "textBlockDesc" and "textBlock" are the control template parts defined in the Fluent theme.
Create your own style and control template.
Since you can't inherit a control template, you have to copy it from the Fluent theme and put it in your resources. The disadvantage is that you'll have to manually resync that template with the original one if there will be an update.
I don't put an example here, because that solution isn't really good too.
Override the menu item style manually.
Set the style of your menu items manually to the "first level" menu items' style.
<Fluent:MenuItem Style="{DynamicResource ApplicationMenuStyle}"/>
So, I'm trying to design as minimalistic a UI as possible, and to that end, I need to provide hints inside textboxes, like android does. I've found many solutions to the problem (see Watermark / hint text / placeholder TextBox in WPF , How can I add a hint text to WPF textbox?) but every solution seems to use lots of XAML code, styles, triggers and the sort. What I want to do is, I want to have a textbox subclass that has a HintText property which I can use everywhere, but so far, I haven't managed to even get close. This is the closest I got:
<TextBox x:Class="MyProgram.CustomControls.HintTextBox"
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"
mc:Ignorable="d" Text="ASDF"
d:DesignHeight="174" d:DesignWidth="708">
<TextBox.Resources>
<VisualBrush x:Key="VB">
<VisualBrush.Visual>
<Label Content="{Binding Path=HintText}" Foreground="LightGray" FontSize="25"/>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Resources>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="{StaticResource VB}"/>
</Style>
</TextBox.Style>
</TextBox>
and:
public partial class HintTextBox : TextBox
{
public HintTextBox()
{
InitializeComponent();
}
public static DependencyProperty HintTextProperty = DependencyProperty.Register("HintText", typeof(String), typeof(HintTextBox));
}
Which is missing the trigger definitions, but that's not the main problem here. My primary problem is that I can't seem to bind the HintText property. I can't assign it through XAML, and I can't bind to it for some reason. I also tried binding to the TextBox's own Text property just to see if it would work, and it didn't. What am I doing wrong? Or am I barking up the wrong tree entirely?
EDIT: I also need the same functionality for a PasswordBox, getting nowhere with that either... Why the hell did they separate TextBox and PasswordBox anyway?
The problem is, that the VisualBrush resource "VB" can be shared between all elements and label content can't be binded.
You can try use my sample TextBox with null text hint
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>
I saw a similar question and hoped for a solution, but simply giving an advice to subclass the ComboBox is not enough for me. I need it in small spoons...
The case is I need an extra button on my special comboBox for adding new records to the item list. I have this as an UserControl today but it doesn't look good and I need more controls on my views, so I started making a custom control trying to extend ComboBox.
I didn't get far... Please lend me a hand... :)
My code so far:
public class ComboBoxWithAdd : ComboBox
{
static ComboBoxWithAdd()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ComboBoxWithAdd), new FrameworkPropertyMetadata(typeof(ComboBoxWithAdd)));
}
}
In Generic.xaml I have this:
<Style TargetType="{x:Type local:ComboBoxWithAdd}" BasedOn="{StaticResource {x:Type ComboBox}}">
</Style>
When making the decision to create a custom control you need to determine whether you need to add actual behavior or just UI. Just adding a button can be done by just customizing the ControlTemplate. It sounds like you want a button that causes an action that will update the Items of the ComboBox which would point to the direction you started down of deriving a control from ComboBox. You'll need to add a few things on the code and XAML side. In your Style you'll need to add a Setter for the ControlTemplate and start with a copy of the default template for ComboBox (I usually do this with Blend but there are other sources out there). You can then add in your new Button wherever you want it in the template.
<Style TargetType="{x:Type local:ComboBoxWithAdd}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ComboBoxWithAdd}">
... copy of default template with your modifications
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
There are a few different ways you could connect the button but the most robust is to use a command that you can bind to in your control code. You can declare your own RoutedCommand in your control code later but to get started just use a built in one.
public ComboBoxWithAdd()
{
CommandBindings.Add(new CommandBinding(ApplicationCommands.New, NewExecutedMethod));
}
Then in the NewExecutedMethod just add whatever logic you want to do the actual action to add an item (probably working with the ComboBox's Items/ItemsSource). To connect the button up just set Command="ApplicationCommands.New". There's a lot more that can be done with a custom control but this should get you started.
Rozon, You can manipulate the Combobox from the code behind like this without creating a complex template:
public class CustomComboBox : ComboBox
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Grid grid = WpfHelper.FindAllChildrenByName<Grid>(this, "MainGrid").SingleOrDefault() as Grid;
if (grid != null)
{
grid.ColumnDefinitions.Add(new ColumnDefinition());
Button button = new Button();
button.Content = "test";
button.SetValue(Grid.ColumnProperty, 2);
grid.Children.Add(button);
}
}
}
WpfHelper is just a class which finds a visual child by its name. Notice that this might break easily if the standard control template of the combobox is changed and MainGrid is renamed.
I would suggest you to use the UserControl that you created by adding one more button and grouped them to make a user control and exposing the required events and commands.
Custom control is not advisable.
But, if you have requirement like that. Here we go:
1) You need to derive the control for what you are trying to extend the capability.
2) You need to create a default Template for the control. [Generic.Xaml]
Rest is your customization.
But, one advantage is you could get a easily Skinnable control.
HTH