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

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.

Related

Data binding to a variable of an object

I'm planning to bind a textbox to a variable of an object
How am I going to do this ?
eg.
<TextBox x:Name="MaxCurrent_Tb" Margin="84,53,0,0" FontFamily="Roboto Light" Height="24" VerticalAlignment="Top" IsReadOnly="True" MaxLines="1" VerticalContentAlignment="Center" HorizontalAlignment="Left" Width="123"/>
class mainWindow(Window) :
wpf.LoadComponent(self, 'TPC_Main.xaml')
sampler = DMMSampler()
self.MaxCurrent_Tb.Text = sampler.max_current
I'm planning to bind max_current of this sampler object,
and max_current is update frequently, how do I binding this varible for real time?
I have set DataContext to another variable, so I can't set DataContext to
sampler, is there anyother way to bind to this sampler object for real time ?

Throws exception when i try to color the text box in WPF

I was trying to set grey color to my text box.I was trying to do the following code but it is throwing an exception as
"Object reference not set to an instance of an object."
because the text box value is null but I want the text box to be in grey color irrespective of the value present or absent.
Here is the code I used to implement the concept..
<TextBox
Name="tbHeadline"
Text="{Binding SelectedStory.Headline, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" Validation.ErrorTemplate="{StaticResource ErrorTemplate}"
Grid.Column="1"
Grid.Row="6"
TextWrapping="NoWrap"
d:LayoutOverrides="Height"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
LostFocus="tbHeadline_LostFocus"
/>
Here is the code I used in Xaml.Cs
tbHeadline.Foreground = Brushes.Gray;
I tried the following code also
tbHeadline.Background = Brushes.Gray;
Both throw the same excepiton.
XAML elements are created during the code-behind's InitializeComponent method so if you're attempting to access a named element before that call it will be null.
Why don't you set the property directly in the XAML markup file? If you do not want to do in XAML file, i would suggest (i usually do it this way):
In the constructor, setup event handler for Loaded event of the whole page or control
Set your initial values for control inside the event handler
Example:
this.Loaded += (sender, e) => {
tbHeadline.Background = Brushes.Gray;
}

Modify content of DataTemplate in code for a Grid control

I have the following Xaml for a grid and I want to be able to modify the value of the Content in code. I want to depending on the condition, not display the "[+]". How do I do that. Also how do i trigger an event to make the change. I am using an Infragistic XamGrid but I think it will apply to other grids too.
<ig:UnboundColumn Key="Sel" x:Name="ubcSel" IsFixed="Left" Width="44" HeaderText=" " PropertyChanged="UnboundColumn_PropertyChanged">
<ig:UnboundColumn.ItemTemplate>
<DataTemplate>
<HyperlinkButton Name="hblSel" Click="hblSel_Click"
Content="[+]" FontWeight="Bold" FontSize="12" HorizontalAlignment="Center" />
</DataTemplate>
</ig:UnboundColumn.ItemTemplate>
</ig:UnboundColumn>
Why don't you use binding on the content property and place the conditional logic in an exposed ViewModel property?

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 content binding problems

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.

Resources