Style property on Blocks/Inlines - Is there a way to get this? - silverlight

I'm using the new RichTextBox control in SL4Beta and want to create styles for paragraphs and runs (blocks and inlines). I noticed that I can create a style for a <Block/>, like so:
<Style x:Key="lvl2Paragraph" TargetType="Block">
<Setter Property="FontFamily" Value="Times New Roman"/>
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="FontSize" Value="22"/>
</Style>
But I can't set that to a <Paragraph/> as The member "Style" member is not recognized or not accessible. Like this:
<RichTextBox TextWrapping="Wrap">
<Paragraph Style="{StaticResource lvl2Paragraph}">
Can't set a style for a paragraph.
</Paragraph>
</RichTextBox>
Is there anyway to make "Style" exposed for the RichTextBox? I'm open to all ideas.

Style is a property and mechanism supported by elements that inherit from FrameworkElement. However the contents of RichTextBox are lightweight, they do not have FrameworkElement or even UIElement in their class ancestory.
The only way I can think of to mitigate this is to create an Attached property to take the place of the missing Style property. However you would have implement in that attached property all the setting of the other properties. It would sensitive to document order if inline Xaml also sets the same properties.

Related

Controls created with XamlReader.Parse don't inherit styles

In my application the user can type in HTML, which then gets converted to XAML. I then parse the XAML using the XamlReader.Parse method and add it to a FlowDocument.
For example, let's suppose I have the XAML for a paragraph stored in a string, and then I parse it and add it to a FlowDocument like so:
var xaml = #"<Paragraph Style=""{DynamicResource Big}"" xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">My paragraph</Paragraph>";
var paragraph = (Paragraph)XamlReader.Parse(xaml);
MyDocument.Blocks.Add(paragraph);
Notice that the paragraph has a Style specified. That Style is defined in the FlowDocument's Resources.
<RichTextBox>
<FlowDocument x:Name="MyDocument">
<FlowDocument.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Foreground"
Value="Red" />
</Style>
<Style TargetType="{x:Type Paragraph}"
BasedOn="{StaticResource {x:Type Paragraph}}"
x:Key="Big">
<Setter Property="FontSize"
Value="24" />
</Style>
</FlowDocument.Resources>
</FlowDocument>
</RichTextBox>
You can see that I've defined two styles. The first is an implicit style, and the second extends the first using the BasedOn attribute. When I dynamically add the Paragraph to the FlowDocument it does pick up the 'Big' Style. However, there's a caveat, it does not pick up the Red Foreground color of the implicit style. How can I make it pick up both?
This only seems to be a problem when I parse the XAML. If I just instantiate a new Paragraph object and add it to the FlowDocument, it does indeed pick up both styles.

DataBinding inside a ContentTemplate inside a Style in Silverlight

I'm defining the following style in XAML:
<Style TargetType="telerik:RadDiagramShape" x:Key="styleShapeBase">
<Setter Property="Width" Value="120" />
<Setter Property="Height" Value="60" />
<Setter Property="IsResizingEnabled" Value="False" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock x:Name="lblName" Text="{Binding Name}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Then in the code-behind I'm assigning the data context. I want to draw a shape with some text in it that comes from an object (if this end up working I'm going to put more info there). I'm doing it like this:
var shape = new RadDiagramShape();
shape.Style = (Style)Resources["styleShapeBase"];
shape.DataContext = item.DataContext;
Where item is a simple POCO that has a Name property of type string (this part works, I've traced it, i.e. the DataContext is correctly assigned).
But the data binding never occurs. Is it by design (i.e. no data binding inside a content template), if not what's wrong? Thanks,
You can use Bindings in your DataTemplates. In this case, the Binding will look for a Name property on whatever you set as the Content of your RadDiagramShape.
You should ensure that your class has this property and that it is a string.
If that still doesn't work, can you post details of how you set the style and Content of each instance of RadDiagramShape, and of the object you are trying to bind to?
Somewhere in the Control Template for the RadDiagramShape class, there will be a ContentPresenter with its ContentTemplate bound to the one you have defined. The problem is that the ContentTemplate is only used if the Content property is also set. Otherwise nothing will be loaded into that ContentPresenter.
To make this work, you must set the Content property on the instance of this element.
This is a good place to start understanding what the DataContext property is

Custom panel with alternate background

I'd like to create a simple custom panel to layout children in a business form fashion. Ideally I'd like my markup to look like this:
<Panels:FormPanel>
<TextBlock Text="Name:"/>
<TextBox />
<TextBlock Text="Address"/>
<TextBlock Text="Unknown"/>
<TextBlock Text="City"/>
<TextBox HorizontalAlignment="Stretch"/>
<TextBlock Text="State"/>
<ComboBox/>
<TextBlock Text="Country"/>
<StackPanel>...</StackPanel>
</Panels:FormPanel>
The panel will layout controls in two columns labels on the left side and values on the right.
I have no problem laying out my controls. The problem is that I also need to alternate background for the rows to create stripes for easier reading.
Any ideas how can this be done?
This doesn't directly answer your question, but you could consider this as another solution to the underlying problem.
Take a look at http://wpg.codeplex.com/. I used a similar property-grid-like control in Windows Forms that was modified to understand custom attributes on my business objects.
Now, in WPF, I would think something similar would work really well if you follow the MVVM pattern and you decorate your ViewModel with attributes that such a property grid understands. Then you don't need to explicitly define the fields like you show above.
You could have a ViewModel:
class PersonViewModel
{
[DisplayName("Name")] // The property Grid uses this the Textblock text
[IsRequired] // The property grid could do validation on the field
[Visible]
public string Name { get; set; }
public long InvisibleSystemField { get; set; } // Not shown
}
And then you'd only have Views (Xaml files) like this:
<myCommon:PropertyGrid DataContext={Binding}/>
It could simply use it's DataContext as the starting point for reflection.
OK I'll stop there for now :)
I'm working on a WPF powered LOB application and I'll possibly build something like this in future.
Implementing a custom panel is not actually that difficult. You have to override two methods, Measure and Arrange. Google for "wpf custom panel" to get some articles about that.
What I would suggest you do to get the behavior exactly as you required in the question is extend Windows.Controls.Grid. Your custom grid could then have two columns by default that you initialize in the constructor and you can programmatically set the Grid.Column and Grid.Row properties on the child controls.
Also worth looking at could be the ItemsControl. It does have support for alternatively colored rows. This example (from MSDN) shows how to use it:
<Grid>
<Grid.Resources>
<Style x:Key="alternatingWithTriggers" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="White"/>
<Style.Triggers>
<Trigger Property="ListBox.AlternationIndex" Value="1">
<Setter Property="Background" Value="CornflowerBlue"/>
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="ListBox.AlternationIndex" Value="2">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="Navy"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ListBox AlternationCount="3" ItemsSource="{StaticResource data}"
ItemContainerStyle="{StaticResource alternatingWithTriggers}">
</ListBox>
</Grid>
You could then specify a template for the items that includes a Label and a TextBox, but getting this to work could be fiddly.
Here's one final thing I'll suggest:
XAML Powertoys include features that allow you to generate business forms from ViewModels, ViewModels from Models and much more. You might need to modify the source to get alternating row colors though.
Good luck!

WPF databind Image.Source in MVVM

I'm using MVVM and am trying to databind the Source property of Image to my ViewModel in such a way that I can change the icon on the fly. What is the best pattern to follow for this? I still have the flexibility to change my ViewModel to suit, but I don't know where to start in either the xaml or ViewModel.
To be clear, I don't want my ViewModel to know about the specific images (that's for the View to know), just the state that triggers different images. For now I have just two states, lets say Red and Green. Should I create an Enum property or a bool? And then how do I databind to switch the image source?
You can use a DataTrigger, and change the image (entirely in XAML) based on the value of a property in your ViewModel. I, personally, would use an enum, since you may want multiple states.
VisualStateManager will work for this as well, but will require WPF Futures or .NET 4.
In order to use a DataTrigger, you can do something like:
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="1.png" />
<Style.Triggers>
<DataTrigger Binding="{Binding ViewModelEnumProperty}" Value="Image2">
<Setter Property="Source" Value="2.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
This will use "1.png", but when your enum is set to "Image2" in the VM, it'd switch to 2.png. More DataTriggers can be added as needed.

Can my WPF Style Setter use a TemplateBinding?

I'm trying to do something like this...
<Style
x:Key="MyBorderStyle"
TargetType="Border">
<Setter
Property="Padding"
Value="{TemplateBinding Padding}" />
</Style>
...but I get the error:
'Padding' member is not valid because it does not have a qualifying type name.
How do I provide a "qualifying type name"?
Note: The reason I'm trying to do this, is that I'd like to include the same Border in a series of similar ControlTemplates.
I also tried this:
<Setter
Property="Padding"
Value="{TemplateBinding GridViewColumnHeader.Padding}" />
...and it actually compiled, but then when I ran the app, I got a XamlParseException:
Cannot convert the value in attribute 'Value' to object of type ''.
I thought maybe qualifying Padding with GridViewColumnHeader (which is the ControlTemplate I want to use this style with) would work, but no dice.
EDIT:
Well, according to the documentation for TemplateBinding, it says:
Links the value of a property in a control template to be the value of some other exposed property on the templated control.
So it sounds like what I'm trying to do is just plain impossible. I really would like to be able create reusable styles for certain controls in my control templates, but I guess the template bindings cannot be included in these styles.
TemplateBinding should work for the case where you're templating a control and you want to bind the value of a property of that control to a property of a different control inside the template. In your case you're templating something (call it MyControl), and that template will include a border whose Padding should be bound to MyControl's padding.
From MSDN documentation:
A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}.
But for whatever reason, specifying TemplatedParent as the source for the binding doesn't seem to work within Style Setters. To get around that you can specify the relative parent to be an AncestorType of the control you're templating (which effectively finds the TemplatedParent providing you haven't embedded other MyControls in the MyControl template).
I used this solution when I was trying to custom template a Button control in which the (String) Content of the Button needed to be bound to the Text property of a TextBlock in the ControlTemplate for the button. Here's what that code looked like:
<StackPanel>
<StackPanel.Resources>
<ControlTemplate x:Key="BarButton" TargetType="{x:Type Button}">
<ControlTemplate.Resources>
<Style TargetType="TextBlock" x:Key="ButtonLabel">
<Setter Property="Text" Value="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type Button}} }" />
</Style>
</ControlTemplate.Resources>
<Grid>
<!-- Other controls here -->
<TextBlock Name="LabelText" Style="{StaticResource ButtonLabel}" />
</Grid>
</ControlTemplate>
</StackPanel.Resources>
<Button Width="100" Content="Label Text Here" Template="{StaticResource BarButton}" />
</StackPanel>
The {TemplateBinding ...} shortcut is not available in a Setter.
But nobody will stop you using the full verbose version such as:
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding}".
A property can be qualified simply by prefixing it with the type name. For example, Border.Padding instead of Padding.
However, I'm not sure it makes sense for your scenario. TemplateBindings are used inside a control template.

Resources