Bind ToggleButton to a bool - c#

I'm trying to implement a toggle button that permits the user to select between linear or logarithmic axis.
For that I have in my View this ToggleButton:
<ToggleButton Width="40" Height="20" Margin="2" Grid.Row="1" Content="LogX" VerticalAlignment="Center" IsChecked="{Binding LogXChecked, Mode=TwoWay}"/>
In my ViewModel:
private bool _isLogXChecked;
public bool IsLogXChecked
{
get
{
return _isLogXChecked;
}
set
{
_isLogXChecked = value;
RaisePropertyChanged("IsLogXChecked");
LogX();
}
}
But with this implementation I can't get it to work, the IsLogXChecked property does not update when the user presses the ToggleButton, and my method LogX() does not fire.
Where might be the problem? Or how should I bind a ToggleButton to a bool? Thank you.

Your XAML binds to LogXChecked, but your ViewModel defines this as IsLogXChecked. Right now, the binding is broken because the property name doesn't match the binding specification.
You can fix this on either side - for example, fix this in the Xaml via:
<ToggleButton Width="40" Height="20" Margin="2"
Grid.Row="1" Content="LogX" VerticalAlignment="Center"
IsChecked="{Binding IsLogXChecked, Mode=TwoWay}" />

Related

How to access selectedItems of a ListBox from ShellViewModel.cs file?

So here is my problem:
I want to access the items (and their properties), which are bound to a ListBox via a Bindable Collection, in a method of another class. (ShellViewModel)
More precise: I want to know which Items in this List are currently selected.
I'm using C#,Caliburn.Micro and the design pattern should be MVVM.
If the Button "Evaluate" is pressed, according to the selected ListBoxItems, different Methods get called.
So the XAML-Code in the ShellView is working fine, and also the "functional" code "works", but it is just working if I code directly in the xaml.cs-file. (big no,no)
Because I'm quite new to this topics, I've tried to create a new instance of the View for the access, but this will just give me control over the ListBox without "knowing" which Items are selected in the original instance.
Here is the general ListBox
<ListBox x:Name="MainListBox" ItemsSource="{Binding SCollection}"
Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="6" Grid.RowSpan="1"
SelectionMode="Multiple"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
MaxHeight="300" MinHeight="200" MaxWidth="340" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="0,5">
<StackPanel>
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Padding="3,5,5,5" />
<TextBlock Text="{Binding Path=Info}" FontWeight="SemiBold" Padding="3,5,5,5" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="Evaluate" Background="Blue" Grid.Column="1" Grid.Row="7" MinWidth="80" Margin="5" Content="Test" Grid.ColumnSpan="1" />
So here are some questions:
Is there a similar solution like the binding of "SelectedItem" via xaml, which is bound to accessible variable, for multiple selected Items?(maybe I just missed something there?)
Is there a way to access the "original" instance of the ListBox(and therefore "live"-selected Items)?
To be clear:
Only problem, besides maybe other better solutions out there, is that, I want the called Method "Evaluate", which is bound to the button, have access to the great knowledge of which Items are selected in my ListBox.
I believe I understood your concern correctly. For the Evaluate method to have the knowledge of which Items has been selected, you can adopt the following strategy.
Add a boolean property in your Collection Type called IsSelected, which would help in tracking the items which are selected. For example,
public class Contact : PropertyChangedBase
{
public string Name { get; set; }
public string Info { get; set; }
public bool IsSelected { get; set; }
}
Now assuming your Collection is defined as following.
public ObservableCollection SCollection { get; set; } = new ObservableCollection();
You can filter the Selected items in Evaluate Method as following.
public void Evaluate()
{
var selected = SCollection.Where(x => x.IsSelected);
}
Update
You would need to Set the IsSelected Property to true during the selection in the List box. This can be done as following.
<ListBox x:Name="MainListBox" ItemsSource="{Binding SCollection}"
SelectionMode="Multiple"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
MaxHeight="300" MinHeight="200" MaxWidth="340" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="0,5">
<StackPanel>
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Padding="3,5,5,5" />
<TextBlock Text="{Binding Path=Info}" FontWeight="SemiBold" Padding="3,5,5,5" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Example Screenshot

UWP Xaml Textblock Data Binding - UI not updating even though the property is updated

Firstly a Disclaimer: I have read dozens of posts with hundreds of answers and yet I have not found a solution. I have attempted to resolve this every which way I can find and yet the UI does not update.
I am writing a UWP app as a personal project. In this particular scenario I have a Grid with an assigned DataContext with a series of TextBlocks which are Bound to Properties. I am using "Binding" and not "x:Bind" due to the Grid been part of a ControlTemplate.
I have implemented the Properties with INotifyPropertyChanged.
I have backing fields.
I have stepped through the debugging and the Properties are updated and retain the values assigned.
I have tried using UpdateSourceTrigger=PropertyChanged.
I have tried different Modes - OneWay/TwoWay etc.
The issue: The UI completely ignores the changes and NEVER updates.
I have set the properties with a value within the Page Constructor, and that is set, but no changes thereafter are applied... EVER.
Please see the relevant code snippets below and maybe someone can see something I have not.
Thanks in advance.
Grid XAML:
<Grid
Grid.Row="1"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,2,2">
<Grid.DataContext>
<views:MenusPage />
</Grid.DataContext>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="152" />
<ColumnDefinition Width="152" />
<ColumnDefinition Width="152" />
<ColumnDefinition Width="152" />
<ColumnDefinition Width="152" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Padding="15,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontWeight="Bold"
Text="Totals" />
<Border
Grid.Row="0"
Grid.Column="3"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding CaloriesTotal, Mode=OneWay}" />
</Border>
<Border
Grid.Row="0"
Grid.Column="4"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding ProteinTotal}" />
</Border>
<Border
Grid.Row="0"
Grid.Column="5"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding FatTotal}" />
</Border>
<Border
Grid.Row="0"
Grid.Column="6"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding CarbTotal}" />
</Border>
<Border
Grid.Row="0"
Grid.Column="7"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="15,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding SugarTotal}" />
</Border>
</Grid>
Properties:
private double _caloriesTotal;
public double CaloriesTotal
{
get { return _caloriesTotal; }
set { Set(ref _caloriesTotal, value); }
}
private double _proteinTotal;
public double ProteinTotal
{
get { return _proteinTotal; }
set { Set(ref _proteinTotal, value); }
}
private double _fatTotal;
public double FatTotal
{
get { return _fatTotal; }
set { Set(ref _fatTotal, value); }
}
private double _carbTotal;
public double CarbTotal
{
get { return _carbTotal; }
set { Set(ref _carbTotal, value); }
}
private double _sugarTotal;
public double SugarTotal
{
get { return _sugarTotal; }
set { Set(ref _sugarTotal, value); }
}
PropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
private void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Code Setting the values:
(NOTE: this is the shortened version but the same effect applies, setting the values in the constructor applies the values and UI updates but setting the values anywhere else on the page, within any method that is executed, they are ignored and the UI shows zeros.)
CaloriesTotal = 10;
ProteinTotal = 20;
FatTotal = 30;
CarbTotal = 40;
SugarTotal = 50;
I found the solution, and while I am dumbfounded as to how it actually worked while the other 'normal' solutions that should have worked did not, is beyond me.
Anyway thanks to #jsmyth886 and #Alexey for your help and suggestions in troubleshooting.
Since posting this question I have tried separating out the properties into another class, referencing <Models:MenuTypesModel x:Key="MenuTotals" /> within the <Page.Resources> block -- failed
I Tried setting the DataContext of the Page itself -- failed
The <Grid> ... </Grid> XAML in my question is, as said, part of a ControlTemplate I even moved the block out of the Control Template and onto the Page itself to eliminate any clashing with other datasources - mind you all different naming structures and paths, so ultimately should not have clashed -- this did not work either, so -- failed
It was #Alexey response that got me thinking in a different line and my searching ultimately led me to this post Binding to Self/'this' in XAML which solved my problem.
I removed the following datacontext from the Grid:
<Grid.DataContext>
<views:MenusPage />
</Grid.DataContext>
And replaced it with this DataContext="{Binding ElementName=_this}" and made sure of my textblock bindings, the end result (Very Short Version):
<Grid ... DataContext="{Binding ElementName=_this}">
...
<TextBlock ... Text="{Binding CaloriesTotal}" />
...
</Grid>
And finally added the a Name attribute to the Page itself:
x:Name="_this"
And it worked!!!!
I have changes nothing in the way I have set my properties.
My properties are still defined as per the question, I have no viewmodels or additional classes, nor am I setting DataContext in the CodeBehind.
Simply adding DataContext="{Binding ElementName=_this}"the grid and x:Name="_this" to the Page allows the UI to reflect the changes to the properties every time.
An oddly simple solution.
A NOTE: though, while I was trying different things though, when I had the Grid on the page itself outside of the ContentTemplate, I set the DataContext of the page in codebehind DataContext = this; and it started populating but still failed to affect the Grid in the Control template.
So unless there is a clash that I didn't see or because I have other DataSources which are unrelated as well as assigned directly the to control that needs it... I cannot figure why it works now with this simple binding.
Anyway, thanks for the assistance and I hope this helps someone else in the future.
If you want to benefit from NavigationCacheMode (Enabled/Required) for the sake of performance, you have to update the bindings on data context change.
public MainPage()
{
this.InitializeComponent();
this.DataContextChanged += (s, e) => this.Bindings.Update();
}
Please use binding DataContext for your main object, not for DataGrid:
<Window.DataContext>
<views:MenusPage />
</Window.DataContext>

How to get value of XAML Control from DataTemplate

I try to get a value from my TextBox on a button click event which is defined in my XAML data template as the following:
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Ausstattung">
<Grid Height="40" Width="Auto" Background="LightSlateGray" >
<TextBlock Grid.Column="0" Foreground="White" FontSize="14" Text="{x:Bind Beschreibung}" HorizontalAlignment="Center" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Column="1" Foreground="White" FontSize="14" Text="{x:Bind Ausgabedatum}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Foreground="White" FontSize="14" Text="{x:Bind Rückgabedatum}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBox Grid.Column="3" Foreground="White" FontSize="14" x:Name="txtAnzahl" PlaceholderText="{x:Bind Anzahl}" TextChanged="TextBox_OnTextChanged" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<StackPanel Orientation="Horizontal" Grid.Column="4" Margin="-25">
//here I want to get the value from the TextBox named "txtAnzahl"
<Button Height="30" Width="30" Margin="0,10,10,10" Padding="0" Click="ButtonBase_OnClick">
<TextBlock FontFamily="Segoe MDL2 Assets" Foreground="LimeGreen" Text="" FontSize="20"/>
</Button>
<Button Height="30" Width="30" Margin="0,10,10,10" Padding="0">
<TextBlock FontFamily="Segoe MDL2 Assets" Foreground="DarkRed" Text="" FontSize="16"/>
</Button>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
So I try to get the value from the Textbox "txtanzahl" on the Button OnClick event.
Here is what it looks like in the live visual tree:
I tried to accomplish it with the VisualTreeHelper but I only found examples of GetChild or GetParent but in this case, it is not a child nor a parent.
Also I cannot get the control with the given name "txtAnzahl" like this:
var anzahl = txtAnzahl.Text;
It says that he didn´t know this element.
You can get the Button's parent (i.e. StackPanel), and then its parent's parent (i.e. Grid), and then go down and find the TextBox.
But... Don't do this. What if you changed the hierarchy, or the type of the Panel?
Since you already know the type (i.e. Ausstattung) of the DataContext of your data template, you should create another property say TextValue and have it two-way bound with the TextBox. Then, you can either get its value from a CommandParameter if you use Button's Command, or in code-behind -
private void ButtonBase_Click(object sender, RoutedEventArgs e)
{
var button = (ButtonBase)sender;
var dataContext = (Ausstattung)button.DataContext;
var value = dataContext.TextValue;
}
Your class needs to implement INotifyPropertyChanged. After that, create a new property like this -
using System.ComponentModel;
using System.Runtime.CompilerServices;
using App1.Annotations;
namespace App1
{
public class Ausstattung : INotifyPropertyChanged
{
private string _textValue;
public string TextValue
{
get => _textValue;
set
{
_textValue = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In your xaml, do this -
<TextBox Text="{x:Bind TextValue, Mode=TwoWay}" Grid.Column="3" Foreground="White" FontSize="14" x:Name="txtAnzahl" PlaceholderText="{x:Bind Anzahl}" TextChanged="TextBox_OnTextChanged" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center"/>
The XAML inside a DataTemplate is a bit special; while it's only written once, at runtime it will be copied and used for each item in the list. This makes it hard to reference the correct element inside the data template. If you have 20 items in the list txtAnzahl might refer to any of them.
The best solution is to put the content inside the data template in a separate UserControl. That way you can reference elements and bind as you normally would.
You can read a bit about UserControl in the official docs.

Radio Button Binding MVVM Application

I have a radiobutton.xaml file that has 4 radio buttons and a button
I showed radio button on the mainwindow by this code
<ContentControl Content="{StaticResource RB}" Height="326" x:Name="select" />
Now I need to implement binding for the radio button
I can't bind the radio buttons and the button to a view model. need to open new windows on behalf of the selected radio button on click of the button.
having difficulty in making V-M for radio button. don't know exactly where to put the binding code ...
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:DiagramDesigner">
<GroupBox x:Key="RB" Header="Select The Architecture Modeling Style" Height="400" >
<StackPanel>
<TextBlock Text="Custom Style Architecture Modeling:" FontSize="20"
Margin="30 30 40 10" HorizontalAlignment="Center" />
<RadioButton Content="Custome Architecture Modeling" Margin="50 0 10 10"
GroupName="Standard" />
<TextBlock Text="Standard Style Architecture Modeling:" FontSize="20"
Margin="30 0 40 10" HorizontalAlignment="Center" />
<RadioButton Content="3-Tier Architecture Modeling" Margin="50 0 10 0"
GroupName="Standard" />
<RadioButton Content="Client-Server Architecture Modeling"
Margin="50 0 10 0" GroupName="Standard" />
<RadioButton Content="Pipeline and Filter Architecture Modeling"
Margin="50 0 10 0" GroupName="Standard" />
<Button Margin="100 20 100 0" Width="200" HorizontalContentAlignment="Center">
<Button.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" Text="Let's Go Draw It..." VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
</Grid>
</Button.Content>
</Button>
</StackPanel>
</GroupBox>
</ResourceDictionary>
need to bind it as MVVM
I'd recommend you to use the ListBox method instead of the one you mentioned. You may find it here:
http://www.codeproject.com/Tips/807025/WPF-MVVM-Binding-for-Multiple-Radio-Buttons
If you'd like to keep the "abstract" groups (custom and standard style architecture modeling), then one of the solution that comes to my mind now is to implement a TextBox in the ControlTemplate and bind it to a property on the view model.
<ListBox ItemsSource="{Binding RadioCollection}" SelectedItem="{Binding SelectedRadio}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<TextBlock Text="{Binding AbstractGroupHeader}" />
<RadioButton
Content="{Binding Header}" ToolTip="{Binding ToolTip}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Margin" Value="5"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
The view model basically is responsible for the view's state (e.g. an animation on the view isn't definied in the view model, but the view model may release, as in start it). VM is a class that you define and it must implement INotifyPropertyChanged interface found in namespace System.ComponentModel in case you want the view to be notified when you change property's value in code. Keep in mind, that the property must have a public access modifier, in order for the binding to work. If you want to follow this method, then read the article located under the link I gave. However, if you want a simpler solution, which will work with your code, then you have to bind IsChecked dependency property of each of the radio buttons to appropriate properties on the view model, like this:
<RadioButton Content="Pipeline and Filter Architecture Modeling"
Margin="50 0 10 0" GroupName="Standard" IsChecked="{Binding IsPipelinedModeling}"/>
And the VM in this case would look like this:
public class SettingsViewModel : INotifyPropertyChanged
{
bool _isPipelinedModeling;
public bool IsPipelinedModeling
{
get { return _isPipelinedModeling; }
set
{
if (_isPipelinedModeling == value)
return;
_isPipelinedModeling = value;
RaisePropertyChanged();
}
}
#region INotifyPropertyChanged implementation
public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion INotifyPropertyChanged implementation
}
To bind the view model to the view you can use either "view first" or "view model first" approach. I'm using view first. In the constructor of the window, user control or whatever you're using, add the following code:
this.Loaded += (s, e) =>
{
this.DataContext = new SettingsViewModel();
};
The code creates a new view model object and sets it as the window's DataContext.
Binding to a button is a little bit different though, because you have to declare a command. It is a class of your own, implementing ICommand interface:
ICommand _drawModelingArchitectureCommand;
public ICommand DrawModelingArchitectureCommand
{
get
{
return _drawModelingArchitectureCommand ?? (_drawModelingArchitectureCommand = new DrawTheModelingArchitectureCommand());
}
}
public class DrawTheModelingArchitectureCommand : ICommand
{
public bool CanExecute(object parameter)
{
// the code that decides whether the button will be enabled or not
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
// the code that is executed when the button is pressed
}
}
And finally the XAML for the button:
<Button Grid.Row="1" Content="Let's Go Draw It..." VerticalAlignment="Bottom" HorizontalAlignment="Center" Command="{Binding DrawTheModelingArchitecture}"/>

WPF Binding a Dependency Property

I am having an issue binding a dependency property in a UserControl. When it initializes it gets a value but then it will not update. I've probably missed something obvious, here are some code snippets:
This is where I bind the BalanceContent dependency property:
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4"
BalanceContent="{Binding Source={StaticResource UserData}, Path=SelectedUser.Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
Here is the TextBox in the UserControl:
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}"
Grid.Row="7"></TextBox>
Here is the Dependency Property:
public static readonly DependencyProperty BalanceContentProperty = DependencyProperty.Register(
"BalanceContent", typeof(string), typeof(PlayerDetails));
public string BalanceContent
{
get
{return (string) GetValue(BalanceContentProperty);}
set
{SetValue(BalanceContentProperty, value);}
}
Here is the list where the selected user is updated, which is in a view that uses the UserControl:
<ListView x:Name="lstAccounts" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Grid.RowSpan="4"
ItemsSource="{Binding Source={StaticResource UserData}, Path=CurrentUserSearch}"
SelectedItem="{Binding Source={StaticResource UserData}, Path=SelectedUser}"
And SelectedUser is defined here in a class that implements INotifyPropertyChanged:
public User SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedUser"));
}
}
The idea is that the TextBox should update when a new user is selected in the list but at the moment it is not doing so. I've put the binding on local TextBox and it updates fine, just not on a DependencyProperty. Any help appreciated.
There are some possibilities you could try:
First, your ListView may not be updating yours ViewModel's SelectedUser property. Try setting the binding in your ListView to "TwoWay" mode:
<ListView x:Name="lstAccounts" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Grid.RowSpan="4"
ItemsSource="{Binding Source={StaticResource UserData}, Path=CurrentUserSearch}"
SelectedItem="{Binding Source={StaticResource UserData}, Path=SelectedUser, Mode=TwoWay}"/>
You can organize better the way the DataContext's are defined. Remember that all the child controls of your UserControl will have access to its DataContext without the use of relative binding (they inherit it). As your PlayerInfo control depends on the SelectedUser, consider setting it's DataContext to the SelectedUser, either binding it to the SelectedUser of the ListView or the SelectedUser in the UserData viewmodel.
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{Binding Source={StaticResource UserData}, Path=SelectedUser}"
BalanceContent="{Binding Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
The source of the current SelectedUser could also be the ListView:
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{Binding SelectedItem, ElementName=lstAccounts}"
BalanceContent="{Binding Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
Either way, you will then be able to do the following on the TextBox, because its DataContext will be the same as the one of its parent:
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding Balance}"
Grid.Row="7"></TextBox>
If the usercontrol depends on the root viewmodel for things like commands and other high level logic, then set the DataContext to it in a way you can easily access the SelectedUser.
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{StaticResource UserData}"
BalanceContent="{Binding SelectedUser.Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
So you can do this:
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding SelectedUser.Balance}"
Grid.Row="7"></TextBox>
In this second approach, however, you will have to check one thing I'm not sure about. I know that when the DataContext of a control changes, it will update all the dependent bindings allright. For example, if you changed the PlayerDetails DataContext to another UserData instance, the BalanceContent property would update as well. However, in this case the BalanceContent depends on property of the SelectedUser of the UserData. So it will listen to Property changes of that instance of User. If the SelectedUser.Balance changes (and User implements INotifyPropertyChanged, or it is a DependencyProperty), BalanceContent will update. Now, if the SelectedUser instance in the UserData changes, I'm not sure BalanceContent will update, because I think that a binding does not listen to changes of every object in its path.
EDIT
The last point was perhaps the first problem I hit when developing with xaml. I had a DataGrid in Silverlight whose entity type had a property of a complex type. One of the columns depended on a property of the complex type. If I changed the value of the complex type, the column would update fine (it implemented INPC). If I changed the complex type instance of an entity, the column would not... The solution was to cascade DataContexts: I created a template column, set the binding of the column for the complex type, instead of its property. Then I bound the text of the TextBox of my template to the property of the complextype, because it was now the TextBox's DataContext.
In your case you can do it for the TextBox.Text, but not for the PlayerDetails.BalanceContent. You can bind the TextBox.DataContext to the SelectedUser of the UserData and then bind Text to the Balance property.
<TextBox VerticalAlignment="Center" DataContext="{Binding SelectedUser}" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding Balance}"
Grid.Row="7"></TextBox>
Please try testing after changing your binding for text box inside your user control to bind to BalanceContent which is User Controls Dependency Property (your original binding source seems to be data context property)
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}"
EDIT: Please try the following code
public User SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedUser"));
OnPropertyChanged(new PropertyChangedEventArgs("SelectedUserBalance"));
}
}
public string SelectedUserBalance
{
get
{
return _selectedUser.Balance;
}
}
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4"
BalanceContent="{Binding Source={StaticResource UserData}, Path=SelectedUserBalance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}"
Grid.Row="7"></TextBox>
The Textbox is trying to bind to the UserControls DataContext. give an x:Name to the Usercontrol definition and set the TextBox's DataContext to DataContext="{Binding ElementName=[YourControlName]}"
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding SelectedItem.BalanceContent, ElementName=lstAccounts}" Grid.Row="7"></TextBox>
forget binding of UserControl , Directly bind TextBox to SelectedItem . I hope this will help.
You are binding a StaticResource to the property. StaticResources, as opposed to DynamicResources, are loaded only once, when the Window (or UserControl) is being initialised. DynamicResources are loaded only when needed (and each time when needed). Any change that is made to a DynamicResource is picked up immediately and the property is updated accordingly. Simply replace the keyword "StaticResource" with "DynamicResource", and the property binding should update each time the resource changes.
Note: If the StaticResource is an object that derives from the Freezable class, it will also behave somewhat like a DynamicResource. For example, if your resource is a Brush, the bound property will update each time you change the Brush object in any way (by changing its opacity, for example), but this is due to the behaviour inherited from the Freezable class. However if you replace the Brush object in the StaticResource by a new Brush object, this change won't be picked up, because the change has been made to the Resource, which is Static, not the Brush.
Also Note: Even resources that are only available as StaticResources, such as data SystemColors, SystemFonts (which provide access to system settings) can be wrapped in a DynamicResource to ensure that the property is updated at each change. This can be done this way:
<Control Property="{DynamicResource {x:Static SystemColors.XXX}}"></Control>.

Categories