Good evening. A WFP binding question please.
Let's say that I have the following three classes:
public class Field
{
public string Data {get; set; }
public string TooltipText {get; set; }
public Field(string data, string tttext)
{
Data = data;
TooltipText = tttext;
}
}
public class FName : Field
{
public FName()
: base("","Enter first name")
{
}
}
public class Person
{
public FName FirstName {get; set; }
}
In XAML, assuming I have set the data context to Person in code, I can bind a TextBox's text and tooltip by
<TextBox Text="{Binding FirstName.Data, Mode=TwoWay}" Tooltip={Binding FirstName.TooltipText}" />
Is there a way to do this generically (if that's the first word) by referencing the base class' properties in a style so that I do not need to declare it for every TextBox? For instance, as a mockup:
<TextBox Binding="FirstName" Style="{StaticResource TextBoxStyle}">
and the style is something like
<Style x:Key="TextBoxStyle" TargetStyle="TextBox">
<Setter Property="Text" Value="{Binding Field.Data, Mode=TwoWay}" />
<Setter Property="Tooltip" Value="{Binding Field.TooltipText}" />
</Style>
All of my input fields are derived from a base class and it would be nice for the binding of data and tooltips to occur automatically from the Style.
Thank you in advance for any knowledge and/or help along these lines.
What you've posted will work fine, just bind directly to Data and TooltipText instead of Field.Data and Field.TooltipText.
I found the answer, which is exactly what you replied! I just need to set the DataContext to FieldName and now it all works perfectly.
<TextBox Style="{StaticResource TextBoxStyle}" DataContext={Binding FirstName}">
This data-binding stuff is so cool.
Thanks again for the help!
Related
I want to bind my custom TreeViewItem to IsExpanded.
The normal way without a custom TreeView Item would look like this.
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded}"></Setter>
</Style>
</TreeView.ItemContainerStyle>
But I want to bind it to my own TreeViewItem for example named CoolTreeItemModel.
CoolTreeItemModel could look like this:
public class CoolTreeItemModel : XY
{
public LocalTreeItemModel()
{
TreeViewItems = new List<CoolTreeItemModel>();
}
public List<CoolTreeItemModel> TreeViewItems { get; set; }
public SomeType IsValid { get; set; }
public bool IsExpanded { get; set; }
}
How CoolTreeItemModel is bound:
<TreeView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding TreeRoots}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="models:CoolTreeItemModel"
ItemsSource="{Binding TreeViewItems}"></HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
How can I bind to the IsExpanded property of CoolTreeItemModel ?
Thanks for your help.
The "normal way" is applicable in this case. Each CoolTreeItemModel will be implicitly wrapped in a TreeViewItem container so you should be able to bind to your IsExpanded property. You may want to set the Mode of the binding to TwoWay though:
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"></Setter>
</Style>
</TreeView.ItemContainerStyle>
Binding requires the use of dependency properties. Dependency Properties On MSDN You would need to define a dependency property like so:
public ClassName
{
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(ClassName));
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
}
From there you can then include the namespace of your class in the top of your xaml and then bind to IsExpanded like normal
<MyNamespace:ClassName IsExpanded="{Binding Value}" />
I Would like to raise one concern of my though. When developers new to XAML/WPF start developing their own controls, the distinction between view data and business data gets muddled. If this is your own control to be consumed by others, there is an entire style template that may need to be created and used. All of this binding should be taking place in this style template, following the example set forth by Microsoft. Also a custom control should have no view model or defined data context as it is expected to be provided by the consumer, meaning a lot of your logic is going to be in the code behind, which is okay as a custom control is only view related and shouldn't have any business logic in it.
I am trying to "throw" several objects on a canvas.
I have 5 types of objects with several properties (like text, position, bitmap, and more)
Each type should be rendered differently (one type will be rendered as textblock, one as bitmapImage, ect.)
I have 5 observableCollections that holds all the objects of same type.
I can bind one of them (the one that represents text for example) to the canvas and using a data template with a textblock to bind each property to the right parameter (like visibility and localtion).
now my 2nd type should be bind to a bitmap.
How can I do that ? How can I bind 5 different types to the canvas and have each type converted to the right element ?
One possible way is to aggregate all collections to a single one... but then it tries to convert everything to the first type...
There`s a great answer to somehow related question that we can extend to make it work with your problem.
Say we have two types that can be dropped to Canvas:
public class TextClass
{
public string Text { get; set; }
}
public class RectangleClass
{
public Brush FillBrush { get; set; }
}
To facilitate the use of collection to bind to we can use the code from answer I mentioned but change ItemTemplate for our custom DataTemplateSelector:
<ItemsControl Name="icMain">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplateSelector>
<TestWPF:CustomTemplateSelector>
<TestWPF:CustomTemplateSelector.TextTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</TestWPF:CustomTemplateSelector.TextTemplate>
<TestWPF:CustomTemplateSelector.RectangleTemplate>
<DataTemplate>
<Rectangle Height="25" Width="25" Fill="{Binding FillBrush}" />
</DataTemplate>
</TestWPF:CustomTemplateSelector.RectangleTemplate>
</TestWPF:CustomTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
And that`s the template selector I used:
public class CustomTemplateSelector: DataTemplateSelector
{
public DataTemplate TextTemplate { get; set; }
public DataTemplate RectangleTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is TextClass)
return TextTemplate;
else if (item is RectangleClass)
return RectangleTemplate;
else return base.SelectTemplate(item, container);
}
}
Well, all that`s left is to bind our collection. I used simple List in code behind just for test:
List<object> aggregation = new List<object>()
{
new TextClass() { Text = "Some test text" },
new RectangleClass() { FillBrush = new SolidColorBrush(Colors.Tomato)}
};
icMain.ItemsSource = aggregation;
This code shows some test text and yummy tomato rectangle. These sample objects do not have any positioning logic, but I figured you have that already.
I am new at WPF and of course I faced some issues with Bindings.
I have initialized RadCartesianChart and I want display data from different type of category lists. For each of those lists I want different color, but I fail to Bind Fill property to my Color property in code behind.
This is how my XAML looks like:
<telerik:RadCartesianChart Name="RevChart">
<telerik:RadCartesianChart.Grid>
<telerik:CartesianChartGrid MajorYLineDashArray="5, 5" MajorLinesVisibility="Y">
<telerik:CartesianChartGrid.MajorYLineStyle>
<Style TargetType="{x:Type Line}">
<Setter Property="Stroke" Value="Gray"/>
</Style>
</telerik:CartesianChartGrid.MajorYLineStyle>
</telerik:CartesianChartGrid>
</telerik:RadCartesianChart.Grid>
<telerik:RadCartesianChart.HorizontalAxis>
<telerik:CategoricalAxis />
</telerik:RadCartesianChart.HorizontalAxis>
<telerik:RadCartesianChart.VerticalAxis>
<telerik:LinearAxis/>
</telerik:RadCartesianChart.VerticalAxis>
<telerik:AreaSeries CategoryBinding="Date" ValueBinding="Rev" Fill="{Binding Color}">
</telerik:AreaSeries>
</telerik:RadCartesianChart>
This is my C# code:
public class Revenue
{
public double Rev { get; set; }
public DateTime Date { get; set; }
public Color Color { get; set; }
}
List<Revenue> list = new List<Revenue>();
...
...
this.RevChart.Series[0].ItemsSource = list;
As a results I am getting correct picture, but color is default. So my binding of Color doesn't work. Is it problem in this line Fill={Binding Color} ? Why?
Its because Fill is a Brush and you are trying to assign a Color, this wont work.
You will have to assign your Color to the Color property of a SolidColorBrush in the Fill property.
Example
<telerik:AreaSeries CategoryBinding="Date" ValueBinding="Rev">
<telerik:AreaSeries.Fill>
<SolidColorBrush Color="{Binding Color}" />
</telerik:AreaSeries.Fill>
</telerik:AreaSeries>
I'd like to dynamically generate some controls in my silverlight application.
To be more clear, here's a simplified definition of my class:
public class TestClass
{
[Display(Name="First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
public List<CustomProperty> CustomProperties { get; set; }
}
Each "CustomProperty" will finally be a TextBox, CheckBox or ComboBox:
public class CustomProperty
{
public CustomDataType DataType { get; set; } //enum:integer, string, datetime, etc
public object Value { get; set; }
public string DisplayName { get; set; }
public string Mappings { get; set; } // Simulating enums' behavior.
}
What is the best way to implement this using MVVM pattern? If I parse CustomProperties in ViewModel, and find out which controls should be created, How can I create new controls in my view based on MVVM pattern.
Is there any silverlight control that can help me make the UI faster?
Can I define data annotations programmatically? for example after parsing the custom property, can I add some data annotations (Display, Validation) to the property and bind it to a DataForm, PropertyGrid or a useful control for this situation?
Thank you.
In these cases you usualy use one of the controls inheriting from ItemsControl (e.g. ListBox) or the ItemsControl directly. The controls inheriting from ItemsControl allow you to define a template for each item in a collection, e.g. using your sample (assuming you got access to your TestClass through a view model):
<ListBox ItemsSource="{Binding TestClass.CustomProperties }">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<!--DataContext is stet to item in the ItemsSource (of type CustomProperty)-->
<StackPanel>
<TextBlock Text="{Binding DisplayName}"/>
<TextBox Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This snippet creates a ListBox that contains a label and a text box for each CustonProperty in your CustomProperties collection.
Given the following XAML snippet:
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="ContextMenuItemStyle">
<Setter Property="MenuItem.Header" Value="{Binding Text}"/>
<Setter Property="MenuItem.ItemsSource" Value="{Binding Children}"/>
<Setter Property="MenuItem.Command" Value="{Binding Command}" />
</Style>
<ContextMenu x:Key="contextMenu" ItemsSource="{Binding MenuOptions}" ItemContainerStyle="{StaticResource ContextMenuItemStyle}" />
</ResourceDictionary>
</UserControl.Resources>
<DockPanel>
<TextBox Height="30" DockPanel.Dock="Top" ContextMenu="{StaticResource contextMenu}" />
<Button Content="Add Menu Item" DockPanel.Dock="Top" Command="{Binding AddMenuItem}" />
</DockPanel>
And View Model:
public class MyViewModel {
public ObservableCollection<MenuItem> DocumentExplorerMenuOptions { get; set; }
MenuItem firstMenuItem;
MenuItem secondMenuItem;
public MyViewModel() {
firstMenuItem = new MenuItem("First") { Command = new DelegatingCommand(x => MessageBox.Show("First Selected") };
secondMenuItem = new MenuItem("Second") { Command = new DelegatingCommand(x => MessageBox.Show("Second Selected") };
MenuOptions = new ObservableCollection<MenuItem> { firstMenuItem, secondMenuItem };
AddMenuItem = new DelegateCommand<object>(x => firstMenuItem.Children.Add(
new MenuItem("Child of First")));
}
public DelegateCommand<object> AddMenuItem { get; set; }
}
And class:
public class MenuItem {
public MenuItem(string text) {
Text = text;
Children = new List<MenuItem>();
}
public string Text { get; set; }
public List<MenuItem> Children { get; private set; }
public ICommand Command { get; set; }
}
Clicking the button does add the child to firstMenuItem but it does not appear in the context menu of the TextBox.
I can't figure out how to make the context menu show the dynamic content of the context menu. Any thoughts?
I would not bind to a collection of MenuItems but rather to a more data-driven collection which may contain the MenuItem header, a command which is executed upon click and another collection of such items for the sub-items. Then you could use a (Hierarchical)DataTemplate to generate the menu on the fly. Doing so would probably take care of update issues if your datatype implements the necessary interfaces.
Edit: You seem to have such a datatype already, could you post its code?
Edit2: I think the problem is that you use a style that explicitly needs to be applied (it is probably only being applied to the main context menu, not the sub-items), as noted before i'd suggest a HierarchicalDataTemplate.
Edit3: lol...
public List<MenuItem> Children { get; private set; }
Of course it's not going to update if it's a List and not an ObservableCollection.
(The class is quite badly designed overall by the way, Lists should normally not even have a private setter, they should be properties with just a getter to a readonly field)