I want to build a custom control in WPF but I don't know what to use. I want to achieve the following:
Control sketch
So there are two different textblocks (header and hint) and also a path for the icon.
I need this control multiple times in one view but the itemssource is not a list (so I can't take an itemscontrol and a datatemplate).
How would I do it the best way?
In Visual Studio there is a template for WPF Custom Controls Library (as well as a template for WPF custom control)
I suppose you're used to creating XAML so the markup will not be covered
To allow binding data items to your control from XAML you need to create dependency properties in your control code:
public string Header {
get => (string) GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);}
public static readonly DependencyProperty HeaderProperty = // note naming - <PropName>+Property
DependencyProperty.Register(nameof(Header), typeof(string), typeof(YourControl), new FrameworkPropertyMetadata(""));
Repeat this code for each property you want to expose
This way you can bind properties in XAML using <YourControl Header="text" /> or <YourControl Header={Binding SomeProp} />
Once you created all the needed props, you can easily bind to them in control's XAML:
<TextBlock Text={Binding Header} />
Related
Right now I am trying to implement a custom control (which is of course way more complex than the demo solution I attached), and I kind of failed at passing a value down via binding.
First of all, the structure of the control:
Control (this is basically a wrapper which passes down properties and handles common functionalities)
Settings (this is a dependency property of the control and is a custom class as well)
Text (this is a dependency property of the settings class)
_renderer (this is a private field of the control which is responsible for rendering the control => in my real control it's just rendering a part of it, but for this example it's enough to just do it like that).
The goal of this is to pass the text from a control like a textbox or something down to the renderer which gets initialized with a reference to the settings.
The XAML which uses the control is written as follows:
<TextBox Text="Initial Text"
x:Name="TextSource"
Grid.Row="0" />
<local:CustomControl Grid.Row="1">
<local:CustomControl.Settings>
<local:CustomControlSettings Text="{Binding Path=Text, ElementName=TextSource, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</local:CustomControl.Settings>
</local:CustomControl>
When I use the same XAML, but give the settings a fixed value for the "Text" then everything works as expected, but as soon as I change it to a binding, I don't even get the initial value any more.
Code with is passing down the text:
Passing the settings to the renderer
private void OnLoaded(object sender, RoutedEventArgs e)
{
_renderer = new ControlRenderer();
_renderer.Initialize(_renderArea, Settings);
}
binding the settings text to the renderers internal text dependency property
SetBinding(TextProperty, new Binding{Path = new PropertyPath(CustomControlSettings.TextProperty), Source = settings});
Note: you can uncomment this without any effect, so this should not be the problem if you ask me.
And hereĀ“s a link to the demo solution I created.
After four hours of pain, later I found the solution. The problem is that the settings-object is not a part of the visual tree and therefore the dependency properties are not resolved.
So to be able to do things like this you need to add the settings to the visual tree. I did that by adding it to the canvas children inside of the CustomControl.
seems like a trivial task: i am building a wpf application, using MVVM pattern. what i want is dynamically change part of a view, using different UserControls, dependent on user input.
let's say, i have got 2 UserControls, one with a button, and another with a label.
in main view i have a container for that. following XAML "works":
<GroupBox Header="container" >
<local:UserControlButton />
</GroupBox>
and a UserControl element with buttons pops up. if i change it to another one, it works too.
question is how to feed that groupbox dynamically. if i put something like that in my model view:
private UserControl _myControl;
public UserControl MyControl
{
get
{
return _myControl;
}
set
{
_myControl= value;
InvokePropertyChanged("MyControl");
}
}
and change my view XAML to something like:
<GroupBox Header="container" >
<ItemsControl ItemsSource="{Binding MyControl}" />
</GroupBox>
and feed it from command with usercontrol for button or for label: nothing happens, although "MyControl" variable is set and is "invoke property changed"..
Obviously there are many ways to skin this particular cat - but to answer the question of why it doesn't work you need to look into the ItemsSource property of ItemsControl on MSDN.
The items control is designed to show multiple items, provided through an IEnumerable passed to the ItemsSource property. You are passing a UserControl, so the binding will fail.
For your example, I would change the ItemsControl to a ContentControl and bind the content to your MyControl property. This should then work.
<GroupBox Header="container" >
<ContentControl Content="{Binding MyControl}" />
</GroupBox>
However, I would strongly recommend looking into other ways of doing this - having a control in your VM breaks MVVM to my mind. Depending on what you are doing look at data templates - #Sheridan's link in the comments provides an great description of a way to do it.
Couldn't post this as a comment so adding as answer..
Have a look at this:
Implementing an own "Factory" for reusing Views in WPF
It uses DataTemplates but doesn't require the DataTemplate section for each view. If you potentially have a lot of user controls/views you wish to display or you are reusing through multiple views or you are intending to actually dynamically generate a view (versus just loading an existing user control) then this might suite your needs.
I've created a simple WPF application which has two Windows. The user fills in some information on the first Window and then clicks Ok which will take them to the second Window. This is working fine but I'm trying to incorporate both Windows into a single Window so just the content changes.
I managed to find this Resource management when changing window content which seems like it is what I'm after. However, I've search for ContentPresenter but couldn't find much help for how I need to use it. For example, if I use a ContentPresenter, where do I put the existing XAML elements that are in the two Windows? I'm guessing the first Window will go into the ContentPresenter but the second one will need to be put somewhere for when it needs to be switched in.
Any help would be great. A simple working example would be even better.
TIA
A ContentPresenter is normally used when restyling existing controls. It is the place where the Content of a control is placed. Instead you should use a ContentControl, which is simply a control that has a content element. Alternatively, you could directly set the Content of your window.
You extract the contents of your two existing windows into two UserControls. Then you create a new Window which will host the contents. Depending on your business logic, you set the content of that window (or that window's ContentControl if you want additional "master" content) to either of those two UserControls.
EDIT:
As a starting point. This is not complete working code, just to get you started. Note that this is bad architecture; you should probably use a MVVM or similar approach once you get this running!
<Window>
<ContentControl Name="ContentHolder" />
</Window>
<UserControl x:Class="MyFirstUserControl" /> <!-- Originally the first window -->
<UserControl x:Class="MySecondUserControl" /> <!-- Originally the second window -->
In code behind of Window:
// Somewhere, ex. in constructor
this.ContentHolder.Content = new MyFirstUserControl;
// Somewhere else, ex. in reaction to user interaction
this.ContentHolder.Content = new MySecondUserControl;
I use ContentPresenter for snapping in content. In the window, I put something like this:
<ContentPresenter Content="{Binding MainContent}" />
In the view model, I have a property called MainContent of type object:
public object MainContent { get { return (object)GetValue(MainContentProperty); } set { SetValue(MainContentProperty, value); } }
public static readonly DependencyProperty MainContentProperty = DependencyProperty.Register("MainContent", typeof(object), typeof(SomeViewModel), new FrameworkPropertyMetadata(null));
Whatever you set MainContent to will show up in the window.
To keep the separation between view and view model, I typically set the MainContent property to another view model and use a data template to map that view model to a view:
<DataTemplate DataType="{x:Type viewmodels:PlanViewModel}">
<views:PlanView />
</DataTemplate>
I put that data template in some central resource dictionary along with a bunch of other view-model-to-view mappers.
I am using the Visifire charts to display data on a Windows Phone 7 application.
I created a chart that was properly bound to a dependency property. It worked great. I decided to make the chart into a user control, since I was going to use it in another project as well with the same setup. Now my databinding doesn't work unless I bind it in the code behind rather than in the XAML.
here's what I have:
<UserControl ... x:Name="root">
...
<chart:DataSeries ... DataSource="{Binding ElementName=root, Path=Results}">
...
</UserControl>
and the code behind:
public MyList Results
{
get { return (MyList)GetValue(ResultsProperty); }
set { SetValue(ResultsProperty, value); }
}
// Using a DependencyProperty as the backing store for Results. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ResultsProperty =
DependencyProperty.Register("Results", typeof(MyList), typeof(MyChart), new PropertyMetadata(null));
public GoogleChart()
{
Loaded += delegate
{
// theChart.Series[0].DataSource = Results;
};
Results = new GoogleResults();
InitializeComponent();
}
If I uncomment the line theChart.Series[0].DataSource = Results; it works perfectly. But if I leave that line commented (like I had before I moved the chart to the UserControl) it doesn't bind. (By the way: theChart is the x:name of the parent of the chart. So the first element, .Series[0], references to the chart).
Does anyone know why this would happen? Again, it worked great until I moved the code to a UserControl.
Thanks
If I'm understanding you correctly you've created this UserControl so that you can place instances of it into various pages in your app.
In that case you are likely to be giving those instances a name. That name will replace the name "Root" that is initially assigned in the UserControl's Xaml. Hence the binding for ElementName=Root will fail.
Typically there is a root element (normally a Grid) with the name "LayoutRoot". Hence instead of relying on the UserControl name which can change use "LayoutRoot" which by convention is the Content element for the UserControl. Like so:-
<chart:DataSeries ... DataSource="{Binding ElementName=LayoutRoot, Path=Parent.Results}">
Note the property path now starts with Parent which takes you up to the UserControl without actually needing to know the UserControl's name.
I have a parent contentcontrol which displays data via a datatemplate. The datatemplate contains a stackpanel with several usercontols of the same type. I like to set the property only once on the parent control, it must set the value of the property on all the subcontrols. But if there is a way to do it on the stackpanel it's also OK. The template can be changed at runtime and the values need also to be propagated to the new template.
My current solution is to implement the property on both parent and subcontrol and use code to propagate the value from the parent to all the subcontrols. My question is: is there a better or other ways of doing this?
EDIT:
Some notes of clarification to my question. The application is currently WPF, but if it's portable to silverlight it would be a bonus. The property is a dependency of the type Style.
I want to use it to style part of the subcontrol. Currently the datatemplate is stored in a separate resource dictionary, so it can be reused. The visuals of the subcontrol are styled via controltemplate. The template contains three different controls, the first one is a label. The need (desire, foolish wish) is to set the style only once, to give the label on all the subcontrols in the datatemplate a consistent look and feel.
So the crux of the problem is to override the value of the a style dependency property on a subcontrol, stored in a resource dictionary from a container control. Both are custom user controls, so all options are open.
<Parent SubSubStyle="x" Template="template" />
<DataTemplate x:Key=template>
<StackPanel>
<Subcontrol SubSubStyle="?"/>
<Subcontrol SubSubStyle="?"/>
<Subcontrol SubSubStyle="?"/>
<Subcontrol SubSubStyle="?"/>
</StackPanel>
</DataTemplate>
Is the property that you're trying to set a DependencyProperty that you have created? If so, the ideal thing to do in WPF is to define the property such that it will be inherited by elements in the visual tree.
If it's not your own dependency property (or if you're using Silverlight which does not support this mechanism) then you should instead use implicit styles.
public class MyControl {
// be prepared for some dependency property hell below
// this defines a DependencyProperty whose value will be inherited
// by child elements in the visual tree that do not override
// the value. An example of such a property is the FontFamily
// property. You can set it on a parent element and it will be
// inherited by child elements that do not override it.
public static readonly DependencyProperty MyInheritedProperty =
DependencyProperty.Register(
"MyInherited",
typeof(string),
typeof(MyControl),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.Inherits
)
);
}
Style is best option http://msdn.microsoft.com/en-us/library/ms745683.aspx#styling_basics