Binding one ElementUI in XAML to 2 source - wpf

<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Textblock Text={Binding Path=Content} Foreground={Binding Path=TextColor}/>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
Hi, I'm developing a book reader application in WP8. I have a stoy with a list of paragraph which I use ListBox to display. Each paragraph content is binding to a textblock as you can see in my code. In Paragraph class, I define a field call TextColor to bind the foreground color of textblock to it. Now, each time user change the color, I have to loop through all paragraph in story and change the value of TextColor. Is there any way to separately bind 2 different property (ie. the Foreground and the Text) of a ListboxItem to different source> So I'll only have to change the Foreground one time. Thank

KooKiz solution is pretty good. However, if you don't want to include any properties to manage foreground colors, or any visual property for that matter, at all you can simply make it a static resource and bind to that instead where you can modify them independently of your model.
For example, you could define a ForegroundResouces class and add in different types of foregrounds your app needs.
In App.xaml
<Application.Resources>
<local:ForegroundResouces xmlns:local="clr-namespace:YOUR-NAMESPACE" x:Key="ForegroundResouces" />
</Application.Resources>
Then define your class
public class ForegroundResouces {
public static Brush TitleForeground { get; set; }
public static Brush ContentForeground { get; set; }
// ...
}
Then define your binding
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Textblock
Text={Binding Path=Content}
Foreground={Binding Path=ContentForeground, Source={StaticResource ForegroundResouces} />
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
Then you can simply change the foreground by modifying your static properties
ForegroundResources.ContentForeground = new SolidBrush(Colors.Red);
You could sort of make different set of themes but this solution is probably only worth it if you have more than one visual property to manage.

There is ways to specify a different source for your bindings. For instance, you can use ElementName to point at your listbox and retrieve its datacontext:
<ListBox x:Name="MyList" ItemsSource="{Binding Path=Paragraphs}">
<ListBox.ItemTemplate>
<DataTemplate>
<Textblock Text="{Binding Path=Content}" Foreground="{Binding Path=DataContext.TextColor, ElementName=MyList}"/>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
But in your case, it's probably easier to just set the Foreground property on the parent list. It will be automatically applies to all child controls:
<ListBox x:Name="MyList" ItemsSource="{Binding Path=Paragraphs}" Foreground="{Binding Path=TextColor}">
<ListBox.ItemTemplate>
<DataTemplate>
<Textblock Text="{Binding Path=Content}" />
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>

Related

DataTemplate Binding depending on property type and with working property Binding

I check those articles about doing DataTemplate :
WPF DataTemplate Binding
WPF DataTemplate and Binding
WPF DataTemplate Textblock binding
and thoses about DataTemplate depending on property type :
WPF DataTemplate Binding depending on the type of a property
Dynamically display a control depending on bound property using WPF
I'm trying to display a property with different controls depending of the property value. I have this Xaml that is partialy working. I have 2 problems :
The property is displaying with the right control, but when I set the value it doesn't go back to the property. Means the "set" of My property is not call (but was before I creates the DataTemplate). I detect that the problem about setting the property is about the ="{Binding Path=.}" but I cannot find the solution to set it otherwise.
Also To be able to make it work, I had to "isolate" the Value into a single ViewModel so that the DataTemplate doesn't affect all the other control.
Can you help me find betters solutions to resolves those 2 problems?
Here is the xaml code of my View linked with MyContainerViewModel that has a "ChangingDataType" :
<UserControl >
<UserControl.Resources>
<!-- DataTemplate for strings -->
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=.}" />
</DataTemplate>
<!-- DataTemplate for Int32 -->
<DataTemplate DataType="{x:Type sys:Int32}">
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="d" MaskType="Numeric" HorizontalAlignment="Stretch"/>
<!--<Slider Maximum="100" Minimum="0" Value="{Binding Path=.}" Width="100" />-->
</DataTemplate>
<!-- DataTemplate for decimals -->
<DataTemplate DataType="{x:Type sys:Decimal}">
<!-- <TextBox Text="{Binding Path=.}" MinWidth="50" HorizontalAlignment="Stretch" />-->
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="f" MaskType="Numeric" HorizontalAlignment="Stretch" />
</DataTemplate>
<!-- DataTemplate for DateTimes -->
<DataTemplate DataType="{x:Type sys:DateTime}">
<DataTemplate.Resources>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</DataTemplate.Resources>
<DatePicker SelectedDate="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</UserControl.Resources>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</UserControl>
More informations about 2 :
I wanted to have in a view a label and a property that changes depending of the object. Something like this :
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo". So I had to create another view that contains the DataTemplate and MyChangingProperty so that the label Allo would not be affected. But the extra View created just for one property is kind of ugly to me, I'm sure there is a better way to isolate the DataTemplate so it can apply only to one UIControl.
<UserControl >
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"/>
</StackPanel>
</UserControl>
Note : MyContainerViewModel here is linked with the first view described.
Thanks in advance!
One possible solution would be to use a DataTemplateSelector. You cannot bind primitive types using two way bindings because that would have to be somehow by reference via the DataTemplate which I think is not supported by WPF.
The DataTemplateSelector now selects the right DataTemplate based on the property type and searches for the right DataTemplate in the resources by name. This also solves your problem that your DataTemplates interacted with the Label.
So first you need to define a DataTemplateSelector that changes the DataTemplate based on the type of the property:
public class MyDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var fe = (FrameworkElement)container;
var prop = (item as MyViewModelType)?.MyChangingProperty;
if (prop is string)
return fe.FindResource("MyStringDT") as DataTemplate;
else if (prop is bool)
return fe.FindResource("MyBoolDT") as DataTemplate;
// More types...
return base.SelectTemplate(item, container);
}
}
Then you need to change the UserControl like this:
<UserControl>
<UserControl.Resources>
<local:MyDataTemplateSelector x:Key="MyDTSelector" />
<!-- DataTemplate for strings -->
<DataTemplate x:Key="MyStringDT">
<TextBox Text="{Binding MyChangingProperty, Mode=TwoWay}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate x:Key="MyBoolDT">
<CheckBox IsChecked="{Binding MyChangingProperty, Mode=TwoWay}" />
<!-- More DataTemplates... -->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"
ContentTemplateSelector="{StaticResource MyDTSelector}" />
</StackPanel>
</UserControl>
You can find a bit more information regarding the DataTemplateSelector here.
You can of course also set a DataType on this new DataTemplates but it isn't required because the x:Key makes them unique anyway. But if you want then it has to look like this:
<DataTemplate x:Key="MyStringDT" DataType="{x:Type local:MyViewModelType}">
In my opinion, the previously posted answer is overkill. While a DateTemplateSelector is a useful thing to know about, it seems unnecessary to me in this scenario.
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo".
The reason it affects the Label object is that the Label object is a ContentControl, and so does the same template-matching behavior for content types as your own ContentPresenter element does. And you've set the content of the Label object to a string value. But you can put anything you want as the content for it.
The way to fix the undesired effect is to intercept that behavior by changing the content from a string object to an explicit TextBlock (the control in the template that a string object normally gets assigned). For example:
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label>
<TextBlock Text="Allo"/>
</Label>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
In that way, you bypass the template-finding behavior (since TextBlock doesn't map to any template and can be used directly), and the content for the Label will just be the TextBlock with the text you want.
This seems like a lot simpler way to fix the issue, than either to create a whole new view or to add a DataTemplateSelector.

WPF binding UserControl collection to ItemsControl. DataTemplate issue

http://www.filedropper.com/wpfapplication9
that link has a project in VS2013 with a sample issue.
my problem is, how to set DataTemplate to UserControl, in ItemsControl.
<ItemsControl ItemsSource="{Binding Collection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
my Collections is a
public IEnumerable<MyUC> Collection {get;}
my MyUC is a
public partial class MyUC : UserControl
{
public string Title { get; set; }
}
When i try to show that collection im getting
MyUC.Content
insted
MyUC.Title
when i change ItemsSouce to ListBox, datatemplate starts working.
but i need to show collection without ListBox addons.
If I understand you correctly, you just want to display the Title property value of your UserControls from your collection. All that you have to do is to declare an appropriate DataTemplate for them in the Resources section:
<DataTemplate DataType="x:Type={YourPrefix:MyUC}">
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
To be honest though, you seem to be going about this in the wrong way. In WPF, we generally don't put UI elements into collections, instead preferring to work with custom data classes that have DataTempates to define what they should look like. In your case, it would look something like this:
<DataTemplate x:Key="TitleTemplate" DataType="x:Type={YourPrefix:MyDataClass}">
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
<DataTemplate x:Key="ControlTemplate" DataType="x:Type={YourPrefix:MyDataClass}">
<YourControlPrefix:MyUC />
</DataTemplate>
You'd then use the TitleTemplate when you want to see just the Title property values and the ControlTemplate when you want to see the whole UserControl.

Binding ListBox Items using Templates

I am going crazy trying to figure this out without success.
I have a DependencyObject, ("UserObject"). It has a "DataItems" DependecyProperty that is an ObservableCollection. "UserDefiniton" is a DependencyObject with a DependencyProperty of "Data". Data has two properties: DataType (an enumeration) and Value (a string).
I am trying to define a ListBox in XAML that uses the "DataItems" property as its ItemsSource. In the ItemTemplate, I have several different controls. For simplicity of this issue, I am using a CheckBox and a TextBox. I want CheckBox to be available and visible when DataType is 0, while I want the TextBox to be available and visible when the DataType is 1. Only one control can be available and visible at a time.
This works:
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=0}"
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
<TextBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=1}"
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<Listbox.ItemTemplate>
</ListBox>
The problem is that even though only one is visible, both are fighting over the Data.Value property (the boolean of the checkbox will show in the textbox, even though the checkbox is hidden).
Basically, though, the binding in this case is working--but the implementation is incorrect.
So, I switched to using Templates. The problem I am having is that I can't get the binding to work.
This is the code that I have for the Template. The Template selector is working correctly, but the Text property of the TextBox and the IsChecked property of the checkbox are not binding to Data.Value:
<DataTemplate x:Key="TextBoxItem">
<TextBox
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</DataTemplate>
<DataTemplate x:Key="CheckBoxItem">
<CheckBox
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
</DataTemplate>
...
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl
Content="{Binding Path=Data.DataType, Mode=OneWay}"
ContentTemplateSelector="{DynamicResource UserDefinitionTemplateSelector}"/>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
So how do I fix the binding?
Content should be set to {Binding}, since the Content will be the DataContext of the data-templates, hence you should just pass on the current DataContext. If you need to pass specific data to the template selector you can just drill down in the whole object.
There also is a template selector on the level of the ListBox, so you do not really need the internal ContentControl.
(You might also be interested in generic methods of debugging data bindings.)

Using data binding on value which is a FrameworkElement

One of my data sources produces a collection of values which are typed to the following interface
public interface IData
{
string Name { get; }
FrameworkElement VisualElement { get; }
}
I'd like to use data binding in WPF to display a collection of IData instances in a TabControl where the Name value becomes the header of the tab and the VisualElement value is displayed as the content of the corresponding tab.
Binding the header is straight forward. I'm stuck though on how to define a template which allows me to display the VisualElement value. I've tried a number of solutions with little success. My best attempt is as follows.
<TabControl ItemsSource="{Binding}">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
How do I display VisualElement here?
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I'm still very new to WPF so I could be missing the obvious here.
ContentPresenters were made for this. The content template becomes:
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding VisualElement}" />
</DataTemplate>
</TabControl.ContentTemplate>
I tested it with a TextBlock and a TextBox.

WPF: Problem with ItemsControl datatemplate as a user control

This works fine:
<ItemsControl ItemsSource="{Binding Persons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text="{Binding Path=LastName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But, I want to make use of a User Control in place of the stack panel with two TextBlocks using something like:
<DataTemplate>
<jasControls:NameView/>
</DataTemplate>
But, with this and a million other syntaxes, the NameView shows nothing, yet it works fine in a separate test, outside the ItemsControl, with explicitly set (dependency) property - e.g.
<jasControls:NameView FName="{Binding Path=Person.FirstName}" />
I can find hardly any examples of this anywhere, and need to know how to specify the properties of the user control - or how can the user control 'receive' the individual item type (a Person)? What syntax to use? Do I need to specify the datatype? Is ItemsControl causing a problem or should any similar control do e.g. ListBox? Can I design it so that the user control gets an entire Person object? What dependency properties do I need in the user control? In short, how to get data into the user control?! The actual business types involved will be a bit more complicated involving some logic in the user control, hence my desire to use a user control.
ANY direction on this will be very gratefully received - TIA
Assuming your Persons collection has many Person objects in it, the DataContext property of your NameView control will automatically be set to the Person object.
You wouldn't need any dependency properties to achieve this feat. Your NameView usercontrol would simply be:
<UserControl ...>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</UserControl>
You should have no codebehind to get this information to display.
From there, you should be able to access the Person object from the DataContext property:
Person person = this.DataContext as Person;

Resources