How to bind two elements to the same property in WPF - C# - c#

I'm using C# in a WPF application with MVVM (with Caliburn Micro framework). I'm trying to bind 2 elements (one TextBlock and one TextBox) to the same property, that resides in my model view. My property is called FirstName.
I have two options to do the binding: Binding Path=FirstName or x:Name=FirstName. When I edit the textbox, I see the changes in the textblock only if I bind in a certain way (see code). Any idea of why the other way does not work? (when I type in the textbox I don't see my textblock updates)
I've tried different mode options (two ways, one way, etc). The NotifyOfPropertyChange seems to be working.
<!-- This works -->
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBox x:Name="FirstName"/>
<!-- This does not work -->
<TextBlock x:Name="FirstName"/>
<TextBox Text="{Binding Path=FirstName, Mode=TwoWay}"/>

With your second example, you need to specify UpdateSourceTrigger=PropertyChanged:
<TextBlock x:Name="FirstName"/>
<TextBox Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Otherwise, the source is only updated when the TextBox loses focus.

Related

ComboBox IsChecked Binding not working in Data Template

I have a ComboBox with a Textblock and a Checkbox displayed to allow me to set a view model boolean property based off the checkbox.
View Code
<ComboBox HorizontalAlignment="Left" IsEditable="True" IsReadOnly="True" Text="-- Filter Columns --">
<ComboBoxItem>
<ComboBoxItem.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Description"/>
<CheckBox IsChecked="{Binding DescriptionHeaderVisibility}"/>
</StackPanel>
</DataTemplate>
</ComboBoxItem.ContentTemplate>
</ComboBoxItem>
</ComboBox>
View Model Property
public bool DescriptionHeaderVisibility
{
get => _descriptionHeaderVisibility;
set => Set(ref _descriptionHeaderVisibility, value);
}
Useful information
I am using MVVM Light
If I do the exact same check box binding somewhere else on the page, it works and notifies my view model of the change.
Binding only does not work within the templated combo box
I am not sure why the binding is not working within the combo box template? Am I just missing something here that I don't know about? If I can get this binding to work properly the plan is to add another few rows of text blocks and check boxes all bound to different boolean properties in my view model.
Picture of drop down box
The problem is, you are using a ContentTemplate, but you do not give it any Content to display. If you just want to use the surrounding DataContext, you could write
<ComboBoxItem Content="{Binding .}">
Inspired by this answer to a similar question.

Can't get custom fonts to display at runtime - Windows Phone 8.1 MVVM - FontAwesome

I am having trouble with custom fonts in my Windows Phone 8.1 MVVM app.
I am using FontAwesome icons. I have included the FontAwesome font file in my project. When I set a static control such as this, it works perfectly;
<TextBlock x:Name="txtTest" Grid.Row="3" Text="" Foreground="Black" FontSize="20" FontFamily="/Assets/Fonts/FontAwesome.ttf#FontAwesome"/>
However, what I need is for this to work dynamically. I have a Hub control on the main page of the app, with ListViews in each Hub section. These are bound to a collection of custom objects, populated from an API response. When creating the collection of objects, the code looks for a marker in the response and dynamically sets the FontAwesome icon depending on the marker.
Hub Section code:
<HubSection x:Uid="hubApproved" Header="Approved"
DataContext="{Binding MyObjects.Approved}"
d:DataContext="{Binding MyObjects.Approved}"
HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}" >
<DataTemplate>
<ListView
ItemsSource="{Binding}"
ItemTemplate="{ThemeResource ApprovedTemplate}"
IsItemClickEnabled="True"
ItemClick="ListView_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
</ListView>
</DataTemplate>
</HubSection>
And here is the Approved Template which binds to this:
<DataTemplate x:Key="ApprovedTemplate">
<StackPanel Margin="0,0,0,19" Background="{x:Null}" >
<TextBlock FontFamily="/Assets/Fonts/FontAwesome.ttf#FontAwesome" Text="{Binding Icon}" Foreground="Black" />
<TextBlock Text="{Binding SupplierName}" Style="{ThemeResource ListViewItemTripNameTextBlockStyle}" />
<TextBlock Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" Text="{Binding StartDate}"></TextBlock>
</StackPanel>
</DataTemplate>
The Template contains a TextBlock which binds to the Icon property of my object. This is supposed to then display the appropriate FontAwesome icon, but instead just displays the unicode of the icon:
I have tried defining the font family of the Hub control from the code behind in the view, but it has no effect:
Hub.FontFamily = new FontFamily("ms-appx:///Assets/Fonts/FontAwesome.otf#FontAwesome");
Any ideas on how to dynamically get these icons to display...? Thanks
You should be able to do it like this:
FontFamily fontFam = new FontFamily("ms-appx:///Assets/Fonts/FontAwesome.otf#FontAwesome");
and set FontFamily like this:
Hub.FontFamily = fontFam
I solved this with a workaround. The icons in my ListView will only ever be 1 of 5 possible icons. So instead of setting the unicode, I created 5 different textbox objects in the template definition, one for each icon. The unicode is static, so the dynamic aspect is instead the Visibility of each object. I created corresponding XAML Visibility properties on the custom object. After this, the style object is bound to its Visibility property, like so:
<!--Generic (shopping cart icon)-->
<TextBlock FontFamily="/Assets/Fonts/FontAwesome.otf#FontAwesome" Grid.Column="0" Text="" Style="{ThemeResource ListViewItemTripNameTextBlockStyle}"
VerticalAlignment="Center" Visibility="{Binding VisGeneric}" />
Then when I create the object collection from the API response, I set the appropriate visibility property to be Visible, according the the marker in the response.
I'd like a slightly more elegant solution than this, but essentially it works...

Why does this ComboBox ignore the DataTemplate when SelectedItem is a ContentControl?

In our application we have a screen design feature which is comprised of a custom ScreenDesignPanel and a Property Grid with a ComboBox at the top which points to the selected item on the ScreenDesignPanel. This allows the user to select the UIElement via the ComboBox or via the mouse to set its properties. We achieve this by binding the ItemsSource of the ComboBox to the ScreenDesignPanel's Children collection, then binding their SelectedItems together. This works great.
However, for whatever reason, if the SelectedItem is a ContentControl or a subclass like Button the ItemTemplate specified for the ComboBox is ignored for the 'selected item area' but it is applied when displaying the item in the dropdown list. If the SelectedItem is not a ContentControl, the template is used in both cases.
This also is seemingly specific to the ComboBox. If we use any other selector control: ListBox, ListView, ItemsControl... even third-party ComboBox controls... they all work as expected, properly applying the DataTemplate. ComboBox is doing something internally which no other control is doing.
Note: Below is an over-simplified example for illustrative purposes of the issue only. It is not how we're actually using it as described above.
Also of note: In the DataTemplate for the ComboBox.ItemTemplate, we are only using properties (i.e. Foreground in the example), and are not displaying the DataContext (i.e. the actual ContentControl) itself. This is important because again, the actual control already exists on the ScreenDesignPanel and therefore can't be used for display in the ComboBox's ItemTemplate as it would have two parents which isn't allowed. In other words, it is being used purely as data here.
One last thing... we have a working solution in our app, which was to wrap the Children before binding it to the ComboBox.ItemsSource. However, I'm still curious as to why the ComboBox behaves the way it does which is SPECIFICALLY what I'm asking. (In other words, I'm not looking for other solutions to this design. We already have a working one. I'm looking for clarity on the odd behavior of the ComboBox itself.)
On to the code!
In the first example below, note how the data template is applied to everything in the dropdown, but the selected item area only uses a template if the selected item is not a ContentControl.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="I am the template" Foreground="{Binding Foreground}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<!-- Four 'Data' items for example only -->
<TextBlock Text="I am a Red TextBox" Foreground="Red"/>
<ListBox Foreground="Purple">
<ListBoxItem>I am a Purple ListBox</ListBoxItem>
</ListBox>
<ContentControl Content="I am a Blue ContentControl" Foreground="Blue" />
<Button Content="I am a Button with Green text" Foreground="Green" />
</ComboBox>
This second example shows that it is completely acceptable and fully supported to use a UIElement as the content of a ContentPresenter and still use a DataTemplate (via ContentTemplate) so you can use it in a purely-data role, allowing the template itself to define the visual appearance without displaying the UIElement itself, which is used purely as data here.
<ContentPresenter>
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBlock Text="I am the ContentTemplate" Foreground="{Binding Foreground}" />
</DataTemplate>
</ContentPresenter.ContentTemplate>
<ContentPresenter.Content>
<Button Content="I am the button" Foreground="Green" />
</ContentPresenter.Content>
</ContentPresenter>
Again, the issue is specific to a ComboBox. I want to find out why the data template isn't applied in that single case, and how to force it to be applied, if possible.
Of note, ComboBox does define SelectionBoxItemTemplate which is separate from the regular ItemTemplate but the rub is that is read-only so you can't set it. We really don't want to re-template the ComboBox as that can mess up proper theming.
Have you tried explicitly setting the DataTemplate to the ContentControl.ContentTemplate property?:
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate">
<TextBlock Text="{Binding Content,
StringFormat='Displayed via template: {0}'}" />
</DataTemplate>
</UserControl.Resources>
...
<ContentControl Content="ContentControl"
ContentTemplate="{StaticResource DataTemplate}" />

How do I hide a cell in a RadGridView depending on the current object's bool property?

I currently have a list of objects in which my RadGridView's ItemsSource is set to. When the property "DoNotContact" of the object in the list has been set to True, I want to hide the information in the cell that contains a Phone number within my RadGridView. As you can see in my XAML, I'm setting the Visibility property within the TextBlock like so:
<telerik:GridViewDataColumn Header="Evening" DataMemberBinding="{Binding Path=EveningPhone}" Width="75" SortMemberPath="EveningPhone">
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Visibility="{Binding Path=DoNotContact, Converter={StaticResource BoolToVisibilityConverter}}">
<Hyperlink Click="MakeEveningCallHandler">
<TextBlock Text="{Binding Path=EveningPhone}" />
</Hyperlink>
</TextBlock>
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewDataColumn>
When attempting to debug it, the Converter is never hit and although I can see the property "DoNotContact" has been set, the phone number still shows. The converter itself works fine as I've used it in other occasions. Again I only want to hide the information WITHIN the cell for the "Evening" Property, not the actual column itself. Any Ideas what's going wrong here? Thanks a bunch!
The code you provided works for me!

WPF Binding to parent DataContext

We have a WPF application with a standard MVVM pattern, leveraging Cinch (and therefore MefedMVVM) for View -> ViewModel resolution. This works well, and I can bind the relevant controls to properties on the ViewModel.
Within a particular View, we have an Infragistics XamGrid. This grid is bound to an ObservableCollection on the ViewModel, and displays the appropriate rows. However, I then have a specific column on this grid which I am trying to bind a TextBox text value to a property on the parent DataContext, rather than the ObservableCollection. This binding is failing.
We've gone through several options here including:
Using AncestorType to track up the tree and bind to the DataContext of the parent UserControl like so (from the great answer to this question, as well as this one)...
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
Specifying the ElementName and trying to target the top level control directly. Have a look here if you'd like to read about using ElementName.
Using a 'proxy' FrameorkElement defined in the resources for the UserControl to try and 'pass in' the context as required. We define the element as below, then reference as a static resource...
<FrameworkElement x:Key="ProxyContext" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}"></FrameworkElement>
In this case the binding finds the FrameworkElement, but can not access anything beyond that (when specifying a Path).
Having read around, it looks quite likely that this is caused by the Infragistics XamGrid building columns outside of the tree. However, even if this is the case, at least options 2 or 3 should work.
Our last thoughts are that it is related to the V - VM binding, but even using Snoop we've yet to find what the exact issue is. I'm by no means an expert with WPF binding so any pointers would be appreciated.
EDIT: I have found some templating examples from Infragistics here that I will try.
EDIT 2: As pointed out by #Dtex, templates are the way to go. Here is the relevant snippet for use with a XamGrid:
<ig:GroupColumn Key="CurrentDate">
<ig:GroupColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DataContext.CurrentDateTest, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</DataTemplate>
</ig:GroupColumn.HeaderTemplate>
<ig:GroupColumn.Columns>
I've left the XML open... you'd simply add the columns you wanted, then close off the relevant tags.
I dont know about XamGrid but that's what i'll do with a standard wpf DataGrid:
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Since the TextBlock and the TextBox specified in the cell templates will be part of the visual tree, you can walk up and find whatever control you need.
Because of things like this, as a general rule of thumb, I try to avoid as much XAML "trickery" as possible and keep the XAML as dumb and simple as possible and do the rest in the ViewModel (or attached properties or IValueConverters etc. if really necessary).
If possible I would give the ViewModel of the current DataContext a reference (i.e. property) to the relevant parent ViewModel
public class ThisViewModel : ViewModelBase
{
TypeOfAncestorViewModel Parent { get; set; }
}
and bind against that directly instead.
<TextBox Text="{Binding Parent}" />

Categories