Subclassing FrameworkElement in Silverlight 4.0 - A Measure Pass Question - c#

I currently have an issue in Silverlight where I want to detect the change in size for an element, and react to it. However, listening with .SizeChanged is actually not sufficient, as often I get a flash of the element at the size it's changing to, before the functionality in the .SizeChanged is called. So I have perhaps a two-fold question.
My intention is to use the Measure pass to calculate the manipulations I want to apply before the size is visually changed, so that I can eliminate this flickering effect. As far as I'm aware, the only way to do this successfully is to create a UIElement that does these calculations on the Measure pass, before Measuring the rest of these elements.
As such, I was hoping to create a really light UIElement by extending off FrameworkElement. However, I can't get the stupid thing to display anything. I'm under the impression that at the FrameworkElement level, a subclass would require adding the content to the VisualTree and I can't seem to work out how to do that.
I was hoping to avoid extending off UserControl, or Panel, as they are far heavier than what I need, with so much extra functionality I dont want. I merely want to catch the Measure pass and perform some work there.
So, is it possible to extended off FrameworkElement in Silverlight 4.0 and actually render something? If not, is it possible to listen for/interrupt the measure pass another way?

You cannot manually add content to the visual tree - this functionality is only exposed to built-in Silverlight controls.
In such a situation, you should derive from Control and specify a default template for this control to use - in the default template, specify the desired contents of the visual tree.
To allow a default template to be used, you should specify DefaultStyleKey in your contructor and give it the value of your control type. E.g. DefaultStyleKey = GetType().
Then, you can specify a style such as this, in e.g. Themes/Generic.xaml
<Style TargetType="my:MyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:MyControl">
<Rectangle Width="100" Height="100" Fill="Red" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Or if your control is a content-presenting control, just derive from ContentControl and it takes care of all the plumbing - you just need to set Content to whatever you wish to display and your subclass will only need to perform the measure-pass override logic.

Related

WPF create your own type

I want to create a template (resource dictionary) for my app. where my type inherits the button type and I then can call it through:
<my-custom-type inherit from button>
</my-custom-type inherit from button>
And of course in WPF.
More specifically, I would like to create copies of the control in the image below with simple XAML syntax as above.
There are two approaches to this, each with their own pros and cons:
Templates allow you to reuse a section of XAML. There is (almost always) no code-behind, and you certainly won't be deriving from Button. For example, if you wanted to have a bordered text box repeated in an ItemsControl:
<DataTemplate x:Key="MyDataTemplate">
<Border>
<TextBox/>
</Border>
</DataTemplate>
Or in a button class you use ContentTemplate:
<Button ContentTemplate={StaticResource MyTemplate}>
</Button>
And you would use it as XTemplate="{StaticResource MyDataTemplate}" in an existing control that used templates. This is usually the way to go. Note that the name of the property won't be Template, but ItemTemplate, or ContentTemplate or something similar.
The exception is if you want custom behavior, in which case you use a UserControl. This technically could inherit from Button though you usually wouldn't. Subclassing a basic control should only be done if you are sure you actually want to do that. Once your user control is created, the syntax would look similar to what you have in your question:
<local:MyButton>
</local:MyButton>
Note that "local" is a made-up xmlns. Your user control would consist of whatever controls you wanted, and you can expose "attributes" to the using code via dependency properties.

How do I implement a WPF control with its own DataTemplate DependencyProperty?

I am an intermediate WPF developer, with working knowledge on how to implement dependency properties as well as simple custom controls. I do not yet understand how I can add a DataTemplate dependency property to a custom control, and use it to define the element tree for each datum in a collection of data.
The full story is that I have been working on creating a WPF map control that displays many different points and geometric shapes on the map, over map tiles. These shapes will translate with the rest of the map when user "drags" the map around.
I have accomplished this, insofar that I have created the map control, and can add child elements to it in Xaml that have map coordinates. I would like to take this farther, and add properties for collections of data, i.e. points, areas, etc. To better understand what I'm looking for, I would like to re-create two properties from ListBox: ItemsSource and ItemTemplate.
I have added two dependency properties to my Map control - PointsSource and PointsTemplate. PointsSource is of type IEnumberable and represents the collection of data to display on the map. PointsTemplate represents what each of those datum should look like. Simply throwing these properties into my control is obviously not enough, but I am unsure of how to coordinate them with one another. If anyone has working knowledge of creating a custom data control with it's own DataTemplate properties for changing the UI tree for each data element, I would really appreciate it.
I have found what I am looking for in the DataTemplate itself. The DataTemplate provides a function for code behind called LoadContent(). LoadContent produces a dependency object that represents the tree of content for a given datum. From what I have found elsewhere, the common use for LoadContent might look like the following:
foreach (object point in PointsSource)
{
FrameworkElement pointElement = _PointsTemplate.LoadContent() as FrameworkElement;
pointElement.DataContext = point;
this.Children.Add(pointElement);
}
The above code will add a content tree for every single element of data, and we give it the datum to bind its DataContext to.
If anyone has working knowledge of creating a custom data control with it's own DataTemplate properties for changing the UI tree for each data element, I would really appreciate it.
Basically, you will want to use an ItemsControl inside your control template, and bind its ItemsSource and ItemTemplate properties to your custom Dependency Properties. Ie,
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<ItemsControl ItemsSource="{TemplateBinding PointsSource}"
ItemTemplate="{TemplateBinding PointsTemplate}"
/>
</ControlTemplate>
</Setter>
</Style>
(assuming the DPs IEnumerable - "PointsSource" and DataTemplate - "PointsTemplate")

Wpf design ; multiple views in one window

Im building an application where I want to head for a design, that could remind of a dockpanel.
What I want, is having buttons in the left side (or left panel) representing different areas of the application (e.g "Milk", "Bread") and then have different "views" in the middle-panel.
What I already have tried, is making an application with a "Frontpage", and buttons changing the whole window/usercontrol - this however will not give me static areas/panels.
I do not want to use a tabcontrol with the tabtitemstrip being vertical - however it is kinda the same functionality im looking to have.
Any ideas?
Below is a picture with the wished design, to kinda give an idea of my thoughts.. Any help appreciated :)
http://s57.photobucket.com/user/RolleKn/media/wpfdesign_zps3737b014.jpg.html
If you use WPF, use ContainerControl or ContentPresenter for that.
In general, "switching Visibility On/Off" is not a good way to go. It forces the UI to create all objects, even those invisible ones, and to handle their data and events, etc.
And you need to switch it all manually.
WPF provides you with many mechanisms that can save you this. Some are smarter than others, some not.
One of the most basic mechanism in WPF is the Control and its Template property. You can replace whole your Grid+Contents+SwitchingVisibility idea with a single Control and switching its Template:
<Window.Resources>
<ControlTemplate x:Key="panel1"> ..carrots.. </ControlTemplate>
<ControlTemplate x:Key="panel2"> ..cucubers.. </ControlTemplate>
<ControlTemplate x:Key="panel3"> ..donkey.. </ControlTemplate>
...
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Control x:Name="foo" />
</Grid>
Now, if you get the foo and set its .Template and set it to panel1, then the "carrots" will show up. if you set it to panel3, donkeys. And so on.
It's very powerful, but it will not be really handy due to some other things I won't cover. There are books and tutorials that explain Templates in depth. Also, this mechanism is really not designed for such task. It's the most basic one, and a good thing to know if you want to work in WPF, but there are more suitable ones here.
Second next powerful and still basic mechanism is ContentControl/ContentPresenter. They work almost in the same way (actually CC uses CP internally), so I'll skip it.
ContentControl is a smart control that knows how to automatically select a correct Template with respect to the data you are tryng to present.
So:
<Window.Resources>
<DataTemplate DataType="CarrotData"> ..carrots.. </..>
<DataTemplate DataType="CucumberData"> ..cucubers.. </..>
<DataTemplate DataType="DonkeyData"> ..donkey.. </..>
...
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ContentControl x:Name="foo" Content="{Binding ..}" />
</Grid>
Note the change from 'ControlTemplate' to 'DataTemplate'.
Now, with this setting, you don't even need to switch templates manually. You just get the "foo" and set its Content to either:
a CarrotData object, that contains the carrot-related data
a CucumberData object, that contains the cucumber-related data
a DonkeyData object, that contains the donkey-related data
Once you set the data to be shown (i.e. foo.Content = carrots[5]), the ContentControl will pick the relevant template to be shown.
You can bind the Content property to just about anything. If you have some dataclass that contains carrots/donkeys and has a property CurrentThing, you can bind to it and ContentControll will switch the views automatically along with the changes to CurrentThing.
That's basics. There's much more to it, in almost any point I tried to briefly cover. For now, leave ControlTemplates. Read about DataTemplates and Bindings. Read about ContentPresenter (shows 1 template for 1 item) and ItemsControl (shows N items+templates). Then, read a little on MVVM pattern.
You will quickly see that "having everything in one Grid" and "switching Visibility" is an odd way to do it.
However, I wouldn't be fair if I didn't mention that everything has a cost included. Extensive use of templates and bindings makes your app a bit slower compared to what you could get when you do everything manually. But usually, doing it manually is just not really worth it.

How to get or set custom object as datacontext instance to custom control in wpf

I've created a custom control that is styled and configures in its own XAML sheet. Databindings in this control uses a specific object (CProject class).
Just to clarify, the control is a project frame, that has controls for settings and a canvas that will be the workspace for each/any project.
The project control (IPProjectPanel) inherits "Frame", and also adds a "settings" stack panel to its children list which in turn contains controls for - well, settings.
The CProject class however, is the pure functional part, with no UI interaction or handling whatsoever. So, I need to "plug" an instance of CProject into every unique project that can be active. So, I want to set a specific instance of CProject as datacontext to every IPProjectPanel instance in a tabpanel. Either I want to set the datacontext by code, or have it created by settings datacontext in XAML, and retrieving it after it has been initialized.
The problem though, is that I cant quite figure out either.
Here is a snippet of the style of the IPProjectPanel in XAML, that uses the approach to set datacontext in XAML:
<Style TargetType="{x:Type ip:IPProjectGrid}">
<Setter Property="OverridesDefaultStyle"
Value="True" />
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ip:IPProjectGrid}">
<Grid Background="White"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="0">
<!---->
<Grid.DataContext>
<ipp:CProject></ipp:CProject>
</Grid.DataContext>
<StackPanel x:Name="PART_settingsPanel"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
MinWidth="300" Background="Gray">
<GroupBox Header="Project settings">
<StackPanel>
....
</style>
Here it is set as a context to Grid, but I'd like to have it as a context of the actual class (IPProjectPanel).
So, the IPProjectPanel instance is created by code (for now..), and I need to retrieve the CProject instance (or set one) so that I can work with it.
I'd like to keep to C#/WPF ways to do stuff, as this app is also training for WPF and C# concepts and such. So the "best C#-WPF" way to do it, is very welcome, but a solution either way!
Thank you for your time.
So in general, the datacontext is primary inteded to be for your ViewModel, and in fact WPF is really set up for doing MVVM (Model View ViewModel) style applications. It's actually fairly simple to learn, but if you're looking for the "Best C#-WPF" way of doing things, take the time to learn MVVM. It's really fairly straightforward.
Simple Example from CodeProject:
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
From Microsoft (somewhat heavy reading):
http://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx
The way you have this set up currently has the potential to create some nasty bugs. You should never declare a DataContext object instance inside a template unless you never plan on accessing it outside of that template's scope. By doing so you will be creating a new instance of the CProject class any time the control needs to be visually re-loaded (like changing tabs) and you may end up referencing an old CProject instance in code while displaying a completely separate one on the screen. Declaring a DataContext object not in a template (i.e. Window.DataContext) is fine.
If you want each control instance to create its own CProject instance you would be better off doing that in code in the constructor and exposing that as a property on the control which you can then bind your Grid.DataContext to inside the template. Avoid setting it to the DataContext property of the control itself as this will cause any implicit source Bindings that are set on the control where it is declared in XAML to break by overriding the inherited DataContext:
Grid.DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PropertyWithCProject}"
It's probably more likely that you will want to control the CProject instances externally and hand them to the control instances. To do this you can create them in a container ViewModel class (MVVM pattern) and set this as a DataContext higher up on something that will contain all of your custom controls. You can then expose individual CProjects or a collection of them and bind your controls' DataContexts to those.

Build Custom Theme or Use Standard Theme in WPF

Soon to be a professional .NET developer (I hope) I start to dig into Windows Presentation Foundation (WPF). Looking into several video tutorials, I find design of GUI a daunting task. Having to specify every color, on every element, in every situation, to every platform seems a bit too much. How can you make this process simpler, and more generic when it comes to design? Is there any templates to start from, or is one expected to specify a couple of hundred rows of XAML before the design is looking appealing?
Considering the code-block below...
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="LightGreen" />
<Setter Property="Foreground" Value="DarkGreen" />
</Style>
... where properties for hover and pushed-button style is left out which need additional rows of XAML to do what the developer wants.
Might there be a simple XAML-editor around to increase productivity? If there isn't, its just to dig dip into XAML and start building styles too keep for later projects.
Designing your own theme is great but it requires a lot of expertise and time. From my point of view its not a great idea to invest in designing your own theme, provided you already have so many themes availabe online. You can take one which suits you and modify it as per your needs.
I genrally refer these links for themes -
WPF Themes -
http://wpfthemes.codeplex.com/
http://wpf.codeplex.com/wikipage?title=WPF%20Themes&ProjectName=wpf
WPF Theme Selector
http://wpfthemeselector.codeplex.com/
Wpf Project With 21 Theme
https://marketplace.visualstudio.com/items?itemName=AliAlikhani.SahaWpfTheme2012
In case you need more options you can buy one here -
http://www.xamltemplates.net/wpf-themes/
There is no requirement to create a Style. You can just use the default style. Consider this simple messagebox style window:
<Window x:Class="MyProject.Test"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test" Height="217" Width="298">
<StackPanel Orientation="Vertical">
<Label>Here is the Message.</Label>
<StackPanel Orientation="Horizontal">
<Button>OK</Button>
<Button>Cancel</Button>
</StackPanel>
</StackPanel>
</Window>
If you really need to re-style controls, I would pick and choose which ones to do. Otherwise, yes I think creating a custom style is a pretty big task.
Creating a custom style is a very large task, however, should you decide this is neccesary (it's not required). You can use Expression Blend to speed up the process.
Reuxables have a couple of free themes you can try. We've just bought one of their non-free ones and it's dead easy, you just throw in a reference in your app.xaml and it transforms your app. Easy to tailor, too.

Categories