How to subclass a WPF ComboBox to add an extra button - c#

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

Related

UWP - Subclassing built-in controls and inheriting styling behaviors

Is it possible to subclass a control (AppBarToggleButton in my case) and "inherit" TargetType of the base class? What I want to achieve is to have a slightly customized AppBarToggleButton (with disabled auto-toggle behavior) put into CommandBar and make it look exactly as if it was regular AppBarToggleButton (i.e. receive style whatever is defined for AppBarToggleButton inside given command bar control template). They say, DefaultStyleKey should help, but it is inherited fine, but, alas, doesn't seem to participate in local style resolution/lookup.
I may need to subclass other controls for various purposes, so the ultimate goal here is to understand how local style resolution works internally and does target instance has any involvement in it or is it a completely external process.
In general, we need make Templated Control for custom AppBarToggleButton. When we make Templated Control with Visual Studio, it will generate Generic.xaml file in the Themes folder that used to declare the custom control's style. And the the custom control cs file like the following.
public sealed class CustomAppBarToggleButton : AppBarToggleButton
{
public CustomAppBarToggleButton()
{
this.DefaultStyleKey = typeof(CustomAppBarToggleButton);
}
}
If you don't want to edit the default style you could remove DefaultStyleKey line that used to binding current control with the style in the Generic.xaml file.
Open Generic.xaml file you will find the following. And it's empty style. If we want to do some small changes, you need copy the complete AppBarToggleButton style to replace it and edit the TargetType to local:CustomAppBarToggleButton. Then you can edit the style base on your requirement.
<Style TargetType="local:CustomAppBarToggleButton" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomAppBarToggleButton">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And if your want to make a new dependency property, please define it in the cs file then use TemplateBinding to bind the property in the style. For more please check this document.
For anyone still stumbling upon this. I managed to solve a similar issue, inheriting from Button, using the approach described here https://stackoverflow.com/a/71338869/10468107
Specifically, adding
<Style BasedOn="{StaticResource DefaultButtonStyle}" TargetType="local:MyButton" />
solved it for me. So maybe it works for other types as well, using {StaticResource Default<TYPE>Style}
I have a similar need and am wondering if the answer is still the same. I have extended the basic ComboBox control to meet some behavioral requirements.
class ExtendedComboBox : ComboBox
I want the ExtendedComboBox instances to inherit the latest platform styling but they are instead getting styled differently. The first of these is an ExtendedComboBox (square corners, larger glyph), while the second is a generic ComboBox (rounded corners, smaller glyph).
The requirement is to have the two combo boxes styled the same way. I am reluctant to create an explicit Style for ExtendedComboBox because then if the style for the generic ComboBox changes the ExtendedComboBox will no longer match. Is there some way to just inherit the standard style?

Display gridcontrol in DXMessageBox

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.

customize Style CheckList RadTreeView

Is there a way to change the style of checkboxes when the ItemsOptionListType="CheckList"
inside a RadTreeView?
There are a couple of ways of doing this that I can think of, but sadly neither of them is particularly easy.
One way is to use Blend or a similar tool to obtain the template for the RadTreeViewItem class. The RadTreeViewItem class and its template are in the Telerik.Windows.Controls.Navigation assembly. Take a copy of this template and modify the CheckBox within this template to customise its appearance as you wish.
To use the template, add a ControlTemplate and a Style to the <UserControl.Resources> element of a XAML page, as follows:
<UserControl.Resources>
<ControlTemplate x:Key="myRadTreeViewItemTemplate" TargetType="telerik:RadTreeViewItem">
<!-- modified template goes here... -->
</ControlTemplate>
<Style TargetType="telerik:RadTreeViewItem">
<Setter Property="Template" Value={StaticResource myRadTreeViewItemTemplate}" />
</Style>
</UserControl.Resources>
This should then apply the modified template to any RadTreeViews in the same XAML file.
Note that we have to use an implicit style (i.e. one without an x:Key), since there seems to be no other way to tell a RadTreeView to apply a given style to its child items.
Alternatively, you can modify a built-in theme. This approach could also change the styles of CheckBoxes used within other Telerik controls in your application, for example in a GridViewCheckBoxColumn within a RadGridView.
EDIT: if you want the template for the CheckBox as used in the RadTreeView by default,
you'll find it in Themes\Office\Black\System.Windows.Controls.xaml within the Telerik.Windows.Controls assembly. This assumes you're using the 'Office Black' theme; adjust the path of this file if you're using a different Telerik theme.

Create template-less user control. OnApplyTemplate

I have "Service" custom control that doesn't need to be visual. I just add it to my view so it can bind to VM and perform some functionality.
Style looks like so:
<Style TargetType="controls:IdattInteractions">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:IdattInteractions">
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When view initialized I don't get OnApplyTemplate called on my control. Is that because ControlTemplate empty? How do I achieve functionality I need? I need to proble visual tree around this control for some functionality and I wanted to do it inside OnApplyTemplate.
How should I go about this?
As far as I know, if your "control" is not visual, then it should be a business logic object of some class, running in memory and interacting with the ViewModel if needed. No need to declare it in XAML.
I'm guessing you "add it to your view" by declaring as a resource. If that's the case, the reason you never receive a call to OnApplyTemplate is because your control is not part of the visual tree. (Test it out: in the debugger, break at the end of your constructor. You'll have a ResourceDictionary with your control living inside. Try to keep following the Parent property until you reach your control. You'll find the Parent will be null quite quickly within the hierarchy.)
To get a call from OnApplyTemplate, you'll need to add your custom control into the root layout container of some other control. Then you'll be inside the visual tree.

Reusing a customized style in Silverlight

By modifying the RowStyle of a DataGrid I have created a customized grid that will display some buttons at the end of the row when the mouse hovers above the row:
I created a new style for DataGridRow based on the default style. I then modified the XAML to add my buttons inside a StackPanel (details omitted):
<UserControl.Resources>
<Style x:Key="DataGridRowStyle" TargetType="swcd:DataGridRow">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="swcd:DataGridRow">
...
<StackPanel x:Name="RowControlsPanel">
<Button>
... these are the buttons displayed on the row
The DataGrid is modified using the style:
<swcd:DataGrid RowStyle="{StaticResource DataGridRowStyle}">
...
</swcd:DataGrid>
I want to create another grid in a similar manner, but with a different set of buttons at the end of the row. I could create a textual copy of my style and modify it accordingly, but I was hoping that I could create a proper reusable class. I'm not sure how to approach this since the stuff I want to factor out of my style is a collection of controls (buttons) inside a style.
My approach so far is to create a MyDataGrid class derived from DataGrid. I have added a new property RowControls to MyDataGrid enabling me to instantiate it like this:
<local:MyDataGrid>
<local:MyDataGrid.RowControls>
<Button>
... these controls should go at the end of the row
</local:MyDataGrid.RowControls>
...
</local:MyDataGrid>
MyDataGrid uses a RowStyle as described above. But how do the contents of the MyDataGrid.RowControls collection get into the Content of RowControlsPanel in the style? I think I should do that in OnApplyTemplate of the DataGridRow, but then I need to derive a new MyDataGridRow class from DataGridRow. Unfortunately it seems that DataGrid is hardcoded to use DataGridRow and I'm not able to inject my own derived row class. I get the feeling that I need to approach my problem of reuse in a different manner, but I'm not sure how?
Customizing simple controls like buttons by adding new properties and modifying the control template is quite easy, but how do I customize a complex control like DataGrid where the template I need to customize is nested inside the grid?
Instead of creating a reusable class you might consider reusing your style with a Silverlight 3 BasedOn style:
http://community.irritatedvowel.com/blogs/pete_browns_blog/archive/2009/03/18/Silverlight-3-1320-BasedOn-Styles.aspx
That technique will allow you to make minor modifications, like changing the row buttons in your example, to an existing style.

Categories

Resources