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}"/>
Related
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 a DataTemplate which contains a TextBox. The DataTemplate is bound to the ContentTemplate property of a Style for the DevExpress FlyoutControl. The Flyout Control itself is within the ControlTemplate of another TextBox.
When the TextBox with the FlyoutControl is focused, I want to redirect focus to the first TextBox in the FlyoutControl's ContentTemplate (from the DataTemplate). Setting FocusManager.FocusedElement={Binding RelativeSource={RelativeSource Self}} on the TextBox I want focused accomplishes this the first time, but once the Flyout has loaded it no longer works.
I have tried every suggestion I can find and nothing so far has worked. I can get the TextBox I want to reference in code and call Focus(), but it always returns false. At best, when I try to focus it in code, the Flyout is focused instead, but never the TextBox within the Flyout.
Here is what each relevant part looks like (irrelevant code omitted):
<DataTemplate x:Key="FlyoutTemplate">
<Grid>
<dxe:TextEdit x:Name="TextThatWantsFocus"
FocusManager.FocusedElement={Binding RelativeSource={RelativeSource Self}}" />
</Grid>
</DataTemplate>
...
<Style x:Key="FlyoutStyle" TargetType="dxe:FlyoutControl">
<Setter Property="ContentTemplate" Value="{StaticResource FlyoutTemplate}"/>
</Style>
...
<dxe:TextEdit>
<dxe:TextEdit.Template>
<ControlTemplate>
<StackPanel>
<dxe:TextEdit x:Name="InnerTextEdit" />
<dxe:FlyoutControl Style="{StaticResource FlyoutStyle}"/>
</StackPanel>
</ControlTemplate>
</dxe:TextEdit.Template>
</dxe:TextEdit>
The flyout is being opened in code. It is here that I also would like to focus the TextBox (TextThatWantsFocus). However, nothing I have tried will give it focus (except for FocusManager handling it the first time), including the typical SO answer involving triggers. Any ideas would be greatly appreciated.
I took DmitryG's advice and submitted a DevExpress support ticket, and they were able to provide a solution.
The issue was resolved by handling the Loaded event of the TextEdit I want focused and using the dispatcher to focus it:
private void TextThatWantsFocus_Loaded(object obj, RoutedEventArgs e)
{
var text = obj as FrameworkElement;
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate()
{ text.Focus(); }));
}
I suggest you using the FocusBehavior from DevExpress MVVM Framework:
<DataTemplate x:Key="FlyoutTemplate">
<Grid>
<dxe:TextEdit>
<dxmvvm:Interaction.Behaviors>
<local:FocusBehavior/>
</dxmvvm:Interaction.Behaviors>
</dxe:TextEdit>
</Grid>
</DataTemplate>
I have a WP7 app that is using bing maps and I have pushpins for multiple locations. On tapping the pushpin I have a tap event that displays a border with some text. To hide the text I’m using a tap event for the map control which sets the border visibility to collapsed:
private void map_Tap(object sender, GestureEventArgs e)
{
this.border.Visibility = Visibility.Collapsed;
}
Now if I am going to be using multiple borders and texts, I’m going to have to set the visibility multiple times like so:
this.border.Visibility = Visibility.Collapsed;
this.border2.Visibility = Visibility.Collapsed;
this.border3.Visibility = Visibility.Collapsed;
So I created a resource for the border in order to refer to just one border.
<phone:PhoneApplicationPage.Resources>
<Style x:Key="BorderStyle" x:Name="border" TargetType="Border" >
<Setter Property="Background" Value="Black" />
</Style>
</phone:PhoneApplicationPage.Resources>
And I set the resource in the pushpin
<my:Pushpin.Content>
<Border Style="{StaticResource BorderStyle}" >
But now when I refer to the border in the map_Tap event I get a System.Windows.Style does not contain a definition for ‘Visibility’ because the border is no long referenced as a UIElement(at least that’s how I’m understanding this). How can a make a style for the border element and still reference it as an element?
I would like to suggest you this approach and this might help you for some other tasks too.
You use a list of type border and add all the border elements into it. Now whenever you want to manipulate all the elements you can have a foreach loop and access any property you would like example item.Visibility and you can manipulate it as you like. This will even help you sort them out so that you can manipulate any specific border.
In WPF (VS2013), I'm creating a button like so:
<Button>
<Label>1</Label>
</Button>
Each of these buttons will have more to it, such as increased font size of the Label, grid row/column assignment, and I might use a binding for the label so that I can change the number. I'm creating a calculator app so I need to reuse this button 10 times (one for each number 0-9). Instead of copying/pasting this button XML 10 times, I wanted to see if I could templatize it.
I've read a little about ControlTemplate and DataTemplate, but I'm not sure if either of these are the correct thing to use. It's also not clear to me what should be a style or what should be a template.
So if someone could help me understand how to "templatize" the button and its styles (e.g. width, height, font size, etc) so that they can be easily reused, that would help a ton. Guidance is appreciated!
Use a ControlTemplate when you want to overwrite the entire template for a control, use a DataTemplate when you want to tell WPF how to draw a data object (usually the DataContext), and use ContentTemplate when you want to tell WPF how to draw the Content property of an object.
Creating a whole new ControlTemplate is quite complex. To demonstrate, check out this MSDN example for an example ControlTemplate for a Button.
In your case, I would recommend creating a Style for your button with setters for common properties such as Height, Width, Font, etc. If you want to draw your button's Content property in a custom way without completely overwriting the button template, include a ContentTemplate style setter to tell WPF how to draw the Button.Content property.
<Button Style="{StaticResource CalculatorButton}" Content="1" />
and
<Style x:Key="CalculatorButton" TargetType="{x:Type Button}">
<Setter Property="Height" Value="50"/>
<Setter Property="Width" Value="50"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding }" FontFamily="Wingdings 3" FontWeight="Bold" FontSize="18" Foreground="Navy" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
The ControlTemplate defines how the button looks, the ContentTemplate defines how the Button.Content looks, and the DataTemplate used for the ContentTemplate is defining how the data object of "1" will be drawn.
You can start with a copy of the style of the button. Use Blend (part of VS) to create that: open the context menu of the button inside the object tree, then select "Edit template" (or similar, don't have an english version at hand), then "Copy of template" (or alike).
Now you may change properties (in designer or XAML). Every button that shall have this style needs to reference this new ressource.
You need to create a new Style of a button. Learning curve is not too steep, but the benefits are enormous. You can start learning about it here: http://msdn.microsoft.com/en-us/library/ms745683(v=vs.110).aspx
Long story short: Open your project with Blend, right-click on your button, "Edit Style", "Edit a copy". If you choose to define it in Application, you can reuse it among other pages (it will be then in you App.xaml file)
Once you have the base style, edit it as much as you need.
I have a custom TextBox control. I am trying to bind it to a simple Description string property of an object. How can I get the binding to work? The same binding works fine if I change this to a TextBox.
<Style x:Key="{x:Type TaskDash:TextBoxWithDescription}" TargetType="{x:Type TaskDash:TextBoxWithDescription}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}">
<TextBox>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public class TextBoxWithDescription : TextBox
{
public TextBoxWithDescription()
{
LabelText = String.Empty;
}
public string LabelText { get; set; }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var textBlock = (TextBlock)this.Template.FindName("LabelText", this);
if (textBlock != null) textBlock.Text = this.LabelText;
}
}
<TaskDash:TextBoxWithDescription Grid.Row="0" Grid.Column="0"
x:Name="textBoxDescription"
Text="{Binding Description, BindsDirectlyToSource=True}" LabelText="Description">
</TaskDash:TextBoxWithDescription>
public partial class EditTaskItem : Window
{
private TaskItem _taskItem;
public EditTaskItem(TaskItem taskItem)
{
InitializeComponent();
this.DataContext = taskItem;
textBoxDescription.DataContext = taskItem;
_taskItem = taskItem;
}
}
Ok ... there are a couple of things wrong with your code.
Lets begin with your style for your custom control. You need to add a static constructor which allows restyling your new control. Like this
static TextBoxWithDescription ()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxWithDescription ), new FrameworkPropertyMetadata(typeof(TextBoxWithDescription )));
}
This tells WPF "Hey, please look for a Style for this control".
Now you can remove the x:Key="{x:Type TaskDash:TextBoxWithDescription}"because this is going to be your default style.
Next thing is. In WPF one thing to understand is, that every control has absolutely no UI content, unless it gets an Template. You already have a Template in your Style, but the only visual content it gets is an empty TextBox. This is strange, because you derive your TextBoxWithDescription from TextBox. So what you create is a a control derived from textbox, containing a textbox.
See this to see how a TextBox Template looks in WPF 4.0.
Back to your empty TextBox in your ControlTemplate. Remember that i said, your controls, without a style are completely invisible, its only logic. The only visible thing is the TextBox in your Template. To make it work somehow, you need to pass some properties to this TextBox. The control says how and the template takes the properties and puts them in use.
You can do this via TemplateBinding
For example. If your control has a Property Background, this property does nothing you can set it as long as you want, but a ControlTemplate can give some meaning to it. So for example add a Rectangle in your ControlTemplate and set the Fill Property to {TemplateBinding Background}. Which basicaly tells the Rectangle "Your property Fill is going to be the value of Background from the control we are currently templating". I hope this makes it clear.
Next thing: You overrid OnApplyTemplate you usually do this to find a control in your ControlTemplate by name. It seems you mixed this with one of your properties. To Find a control in your Template via FindName, you need to give it a name
Like this
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}">
<TextBox x:Name="PART_MyTextBox">
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
and modify your OnApplyTemplate to
var textBlock = (TextBlock)this.Template.FindName("PART_MyTextBox", this);
Now you have the textblock in your current ControlTemplate.
The last mistake i can see is.
You set in OnApplyTemplatethe TextBox Text to your LabelText. While this works, one time, it is not very nice and usually not the WPF way. Also if you modify the LabelText property, it will not be displayed, because you would have to set it again.
Change your LabelText to a dependency property Now you can use the already mentioned TemplateBinding to set this text, directly on your control template TextBox.
<TextBox x:Name="PART_MyTextBox" Text="{TemplateBinding LabelText}>
</TextBox>
This will also make sure that changes to your Control property, will also update the Text on this TextBox.
One last thing
this.DataContext = taskItem;
// textBoxDescription.DataContext = taskItem;
_taskItem = taskItem;
If your textboxdescription is going to be a parent of your window, you don't need to set the DataContext explicitly, because it will be inherited down the hierachy. As long as an Element don't change the DataContext, it will be always the DataContext of the parent.
I would suggest you read more on the basics of WPF, i know it has a steep learning curve, but its worth the effort. It is difficult if someone comes from a WinForms background to get the head wrapped around all the different new design philosophies and different ways to do things.
Hope that helps a bit
#dowhilefor - great answer. I write this as an answer only because it's going to be too long for a comment.
#Shawn - it seems like you are trying to make a Label-Field control. If that's the case, try this template:
<ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}">
<Grid>
<Grid.ColumnDefinitions>
<!--The SharedSizeGroup property will allow us to align all text boxes to the same vertical line-->
<ColumnDefinition Width="Auto"
SharedSizeGroup="LabelColumn"/>
<!--This column acts as a margin between the label and the text box-->
<ColumnDefinition Width="5"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{TemplateBinding LabelText}"
VerticalAlignment="Center"/>
<Border Grid.Column="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="PART_ContentHost"
Padding="{TemplateBinding Padding}"/>
</Border>
</Grid>
</ControlTemplate>
And remove the override for OnApplyTemplate.
If the control is a part of a (often called) "PropertyGrid" and you have multiple instances of the TextBoxWithDescription control in the same panel, define Grid.IsSharedSizeScope on that panel (it doesn't have to be Grid panel). This will align the text boxes to a uniform vertical line.