Silverlight Label content binding problems - silverlight

I'll preface this and say that I'm new to Silverlight development by about week so I'm most likely doing it wrong...
Anyway I have a Label and a TextBox done up thusly in XAML:
<dataInput:Label Target="{Binding ElementName=JobCode}" Height="18" HorizontalAlignment="Left" Margin="15,7,0,0" Name="lableJobCode" VerticalAlignment="Top" Width="250" FontWeight="Bold" Grid.Column="1" />
<TextBox Height="23" Text="{Binding SelectedRole.Job_Code}" HorizontalAlignment="Left" Margin="15,31,0,0" Name="JobCode" VerticalAlignment="Top" Width="277" Grid.Column="1" IsReadOnly="{Binding IsNotAdmin}" />
Everything works great, the only issue I have is that the binding I'm doing on the IsReadOnly attribute which goes to a boolean in my ViewModel which is set based on a call to an authentication service, is now overriding the label Content to the name of my ViewModel property: IsNotAdmin. I can't seem to find a way to specify which data binding source to pull the label content MetaData from. Maybe I'm missing something on how to manipulate control editablity/visibility from my ViewModel.
--Update: The data source class that the TextBox is bound to is as follows (for the relevant parts):
public class RoleSummary {
[Display(Name= "Job Code (To be Completed by HR):")]
public string Job_Code { get; set; }
Without the binding to the IsReadOnly attribute the Label displays the text from the data annotation just fine. When I add the binding it displays "IsNotAdmin"

can you post more of your code? I'm not entirely sure what it is that you're trying to make happen so it's hard to propose a solution.
I assume you're trying to create a text entry element that has validation performed on it (hence the label) -- but what exactly is the label supposed to be showing for it's content?
EDIT: I figured this out. The label control by default looks through all the properties in its datacontext looking for metadata it can use. For whatever reason it decided to use the metadata for the IsNotAdmin property in your code (even though you didn't set it manually, I assume that the Display metadata gets a default value of the property name), and so you get that for the text of the label.
Microsoft put in a property specifier into the data controls so you can tell it which property it should use for the metadata lookup: PropertyPath
Try it like this:
<dataInput:Label Target="{Binding ElementName=JobCode}" PropertyPath="SelectedRole.Job_Code" Height="18" HorizontalAlignment="Left" Margin="15,7,0,0" Name="lableJobCode" VerticalAlignment="Top" Width="250" FontWeight="Bold" Grid.Column="1" />
<TextBox Height="23" Text="{Binding SelectedRole.Job_Code}" HorizontalAlignment="Left" Margin="15,31,0,0" Name="JobCode" VerticalAlignment="Top" Width="277" Grid.Column="1" IsReadOnly="{Binding IsNotAdmin}" />
As long as your datacontext is right (which it should be) this should work for you -- it worked in my sample I reconstructed from your code.

Related

How to keep bindings when serializing a WPF usercontrol into an XElement

I'm trying to serialize a custom WPF user control that features a grid with a textblock that is bound to a dependency property named "Frequency". The snippet that defines the textblock is as follows (the definition of the user control is quite lengthy to post it here!):
<TextBlock x:Name="FrequencyText" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
FontFamily="Arial Rounded MT"
Foreground="White" FontSize="10" FontWeight="DemiBold" Margin="3"
TextBlock.Text="{Binding Frequency, ElementName=FrequencyButtonControlA, Path=Frequency}"
TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Left" />
I'm working on an editor tool that enables the user to arrange some custom controls into a canvas and change their properties and I need to provide the user with the capability to clone an existing element. In order to do this, I'm serializing the control into an XElement and instantiating a new one afterwards. The code to perform the serialization is as follows:
var contentXaml = (commsPanelItem.Content != null) ? XamlWriter.Save(commsPanelItem.Content) : string.Empty;
var serializedItem = new XElement(
"CommsPanelItem",
new XElement("Type", commsPanelItem.GetType().ToString()),
new XElement("Left", Canvas.GetLeft(commsPanelItem.UIElement)),
new XElement("Top", Canvas.GetTop(commsPanelItem.UIElement)),
new XElement("Width", commsPanelItem.Width),
new XElement("Height", commsPanelItem.Height),
new XElement("zIndex", Panel.GetZIndex(commsPanelItem.UIElement)),
new XElement("Content", contentXaml),
commsPanelItem.GetAttributesForSavingPanelInConfigurator() as XElement);
return serializedItem;
The problem I'm having is that, when serializing the textblock, the "Text" field doesn't preserve the binding to the "Frequency" dependency property, it just gets exported with the value assigned by the user ("131.5" in the following example):
TextBlock Text="131.5" FontFamily="Arial Rounded MT" FontWeight="SemiBold" FontSize="10" Foreground="#FFFFFFFF" TextAlignment="Center" Name="FrequencyText" Margin="3,3,3,3" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2"
My question is: is there any way to be able to serialize dependency property's binding instead of the actual value?
Thanks in advance!
Use the "GetBinding" method found on FrameworkElements instead of pulling its value:
BindingExpression bindingExpression = myTextBox.GetBindingExpression(TextBox.TextProperty);
Then you can probably serialize the BindingExpression.

issues regarding textboxes

I have two textboxes which binds to the same property. One textbox is for the original text, the second is for the new text. What I'm having issue is, when the first textbox's text gets submitted, it returns the original text. But when I type in the second textbox to update the name, this automatically override what's in the first textbox's text. I was just wondering if there's anyway of stopping this so I'll have original and new text displaying.
<TextBox Name="txtOriginalName"
HorizontalAlignment="Right" VerticalAlignment="Top"
Width="524" Height="auto" TextWrapping="Wrap"
AcceptsReturn="True" HorizontalScrollBarVisibility="Auto"
Text="{Binding Path=Person.Name}"/>
<TextBox Name="txtNewName"
HorizontalAlignment="Right" VerticalAlignment="Top"
Width="524" Height="auto" TextWrapping="Wrap" AcceptsReturn="True"
HorizontalScrollBarVisibility="Auto"
Text="{Binding Path=Person.Name}"/>
Try Text="{Binding Path=Person.Name, Mode=OneTime}" for txtOriginalName, then txtOriginalName will be initialised to the value in Person.Name, but won't be updated when Person.Name changes later.
Here's a useful binding cheat sheet
Ideally you have a PersonViewModel that has separate OriginalName and NewName properties, with some trigger condition that determines when the underlying Person.Name is updated. This also allows you to put UI-level validation logic (with associated feedback) if certain name entries are invalid. You're essentially decoupling the process of updating a Name (with a notion of New/Original names) from the process of containing a Name. The former can be in a state where the Name is in the process of changing, whereas the latter simply has a Name.

WPF - What to do when a Dependency Property is another Control in the XAML

I'm hoping this will be my last question today. I'm in a hurry and google is not helping much (that or I'm searching the wrong places).
I created some custom properties and behaviors so my RadioButtons can alter my labels Content and the mask of my TextBoxes.
I could pass a String as a property, but how do I pass another control as a property? (AdjustedLabel is of type Label)
<RadioButton i:CPF_CNPJAdjustBehavior.LabelContent="Apple" i:CPF_CNPJAdjustBehavior.AdjustedLabel="??????????" Content="CPF" Height="16" HorizontalAlignment="Left" Margin="30,216,0,0" Name="radioButton1" VerticalAlignment="Top" GroupName="a" IsChecked="True">
<int:Interaction.Behaviors>
<i:CPF_CNPJAdjustBehavior/>
</int:Interaction.Behaviors>
</RadioButton>
<Label Content="Label" Height="28" HorizontalAlignment="Left" Margin="20,81,0,0" Name="MyLabel" VerticalAlignment="Top" />
What do I have do write in "?????????" to set AdjustedLabel to the label named "MyLabel" ?
Thanks in Advance
Clark
AdjustedLabel="{Binding ElementName=MyLabel}" will do what you're searching for.
Consider reading some documentation for getting started with WPF, Bindings, Dependency Properties and XAML syntax. You could start with XAML Syntax In Detail, Dependency Properties Overview and Data Binding Overview.

Databinding between XML file and GUI

I've got a problem with my little app here that is conceptually very simple. I have an XML file that essentially just maps strings to strings.
Long-winded explanation warning
A sample file would look something like this:
<Candies>
<Sees>Box1</Sees>
<Hersheys>Box2</Hersheys>
<Godiva>Box3</Godiva>
</Candies>
Although I could use a different schema, like:
<Candies>
<Candy>
<Name>Sees</Name>
<Location>Box1</Location>
</Candy>
</Candies>
...I opted not to, since the former didn't have any forseeable adverse side effects.
In code behind, I load the contents of my XML file into an XDocument with LINQ. I also have a List variable defined, because this is what I'm databinding my GUI to. CandyLocation looks like this:
public class CandyLocation
{
public string Brand { get; set; }
public string Location { get; set; }
}
And my simple GUI is just this:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="Auto" Width="Auto">
<Page.Resources>
<DataTemplate x:Key="CandyTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Brand}" Margin="3"></TextBox>
<ComboBox Grid.Column="1" SelectedValue="{Binding Location}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}, Path=DataContext.LocationNames}" Text="{Binding Location, Mode=TwoWay}" Margin="3"></ComboBox>
</Grid>
</DataTemplate>
</Page.Resources>
<DockPanel>
<Button DockPanel.Dock="Bottom" Margin="3" Command="{Binding SaveCandiesCommand}">Apply Changes</Button>
<Button DockPanel.Dock="Bottom" Margin="3" Command="{Binding AddNewCandyCommand}">Add Candy</Button>
<ListBox DockPanel.Dock="Top" ItemsSource="{Binding CandyLocations}" ItemTemplate="{StaticResource CandyTemplate}" />
</DockPanel>
</Page>
So the overview is this:
The application loads and then uses LINQ to load the XML file. When the GUI is presented, it calls "GetCandyLocations", which traverses the XML data and populates the List object with the contents of the XML file. Upon initial loading of the XML, the GUI renders properly (i.e. the candy brands and their locations appear correctly), but that's where the success story ends.
If I start from a blank XML file and add a brand, I do so by adding a new XElement to my XDocument root. Then I call OnPropertyChanged( "CandyLocations") to make the GUI update. The initial value for Location is "", so it's up to the user to select a valid location from the combobox. The problem is, I can't figure out how to get their selection databound correctly, such that I can update the XElement value. Because of this, when I save the candy locations, everything ends up with a blank location value. In addition, anytime the user clicks Add Candy, all of the previously selected location comboboxes get blanked out.
In summary:
How should I handle the selection change in the GUI? I am using MVVM for this application, so I have avoided using the ComboBox's SelectionChanged event.
Is there a way to databind directly from the GUI to the XDocument? I haven't tried it yet, but it would be best to avoid having multiple sources of data (i.e. XDocument for serialization and List for GUI rendering). Perhaps I can have the getter return the result of a LINQ query and pair it with a value converter???
How would you change my implementation if you were to write this application? I'm still learning MVVM and WPF, so any advice would be really great.
Thanks!
On your ComboBox, it looks like you might be getting a conflict between the SelectedValue and Text properties. Text is usually only used with IsEditable="True". Try using just SelectedItem:
<ComboBox SelectedItem="{Binding Location}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}, Path=DataContext.LocationNames}" ></ComboBox>
If you want to use the XDocument directly as your data source you can use this (assuming XDocument is exposed from the VM as AvailableLocations):
<ComboBox ItemsSource="{Binding Path=AvailableLocations.Root.Elements}" SelectedValue="{Binding Location}"
SelectedValuePath="Value" DockPanel.Dock="Top" DisplayMemberPath="Value"/>
If you'd rather do something like display the company names, just change DisplayMemberPath to "Name".
Also try using an ObservableCollection instead of a List for CandyLocations so you can get automatic change notifications when items are added or removed.

Silverlight: Label does not evaluate Binding of custom DependencyProperty

I have a Silverlight 3 Label which I connect to a ComboBox using the Target Property of the Label. According to MSDN the Label class iterates through the targets bindings and searches the sources for meta data to determine the content of the Label.
This actually works as long as the target is a standard control. But if I use a custom control, which in my case extends ComboBox, and introduce a new DependencyProperty it is simply ignored.
E.g. this works:
<dataInput:Label Grid.Row="3" Grid.Column="0"
Target="{Binding ElementName=cbxCountry}"
VerticalAlignment="Center"/>
<ComboBox x:Name="cbxCountry" DisplayMemberPath="Name"
SelectedItem="{Binding DataModel.Country, Mode=TwoWay}"
ItemsSource="{Binding Countries, Source={StaticResource ApplicationData}}"/>
In the above example the SelectedItem Binding is searched and DataModel.Country does contain the DisplayName which is taken.
But this does not:
<dataInput:Label Grid.Row="3" Grid.Column="0"
Target="{Binding ElementName=cbxCountry}"
VerticalAlignment="Center"/>
<local:MyComboBox x:Name="cbxCountry" DisplayMemberPath="Name"
MySelectedItem="{Binding DataModel.Country, Mode=TwoWay}"
MyItemsSource="{Binding Countries, Source={StaticResource ApplicationData}}"/>
The custom Properties are Dependency Properties and declared as follws:
private static readonly DependencyProperty MySelectedItemProperty =
DependencyProperty.Register("MySelectedItem",
typeof(object), typeof(MyComboBox),
new PropertyMetadata(null,
MySelectedItemPropertyChanged));
I know that I can work around this by defining the PropertyPath on the Label, but I'd rather avoid this, if possible.
So my question now is, can anyone reproduce this issue, and much more important of course, does anybody know how to solve it? :-)
Thanks, Markus
Ok, in case anyone runs into the same problem, here is the solution: Just change the visibility of the DependencyProperty from private to public.
Quite obvious actually... :-/

Resources