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.
Related
I have binding in TextBox
<TextBox x:Name="TbxActiveSourceNameSourceNameSourceName" IsEnabled="True" Text="{Binding ViewAudioAudio_ActiveSourceNameModel.ActiveSourceName_SourceName,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Padding="0,-2,0,0" Height="15" VerticalAlignment="top" Margin="127,10,0,0" FontSize="10" BorderBrush="#FF918D8D" TextAlignment="Center" Width="75px" HorizontalAlignment="Left" HorizontalContentAlignment="Right" />
But characters run from right-to-left.
How do I must to characters run from left-to-right(normal) when I input words in textBox
Many Thanks
I know this is old question but if anyone runs into this issue it happens to deal with
<TextBox Text={..., UpdateSourceTrigger=PropertyChanged}
it seems that when the property changes via keystroke it automatically detects the property change and so it updates the property it's binding to as well. Because of this it resets the cursor to the very left of the textbox rather than leaving where it was last left off.
Or at least I've been only able to reproduce this issue by changing a textbox to textblock and then back to a textbox and changing the UpdateSourceTrigger to a type that isn't PropertyChanged and would correct the issue and have the text flow right to left.
Set the FlowDirection property to LeftToRight. Reference here.
I have an ObservableCollection of addresses that I am binding to a ListBox. Then in the ItemTemplate I am Binding to the current address record using {Binding .}. This results in my addresses displaying using their ToString method which I have setup to format the address. All is good, except if I update properties on an individual address record the list in the UI does not update. Adds/Deletes to the list do update the UI (using the ObservableCollection behavior). If I bind directly to properties on the address the UI does update (using the INotifyPropertyChanged behavior of the Address object).
My question is, is there a way to notify the UI of the change to the object as a whole so that I can still use this syntax or do I need to punt and put a DisplayText property on my address type that calls the ToString method and bind to that? FYI, this is an MVVM architecture so I don't have the luxury of calling Refresh on the ListBox directly.
Thanks for any help/ideas.
<ListBox x:Name="AddressList" ItemsSource="{Binding Addresses}" Background="Transparent" BorderBrush="Transparent"
Width="200" HorizontalAlignment="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding .}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When you bind to the Address object itself, the object itself -- that is, its identity -- doesn't change, even though its properties do. WPF therefore doesn't know to refresh the binding in this case.
So yes, you need to bind to a notifying property (or properties) rather than the whole object. As you say, one way to do this is to create a DisplayText property, and raise the PropertyChanged event for that property whenever something that affects the display text changes. Another is to use multiple TextBlocks in a horizontally oriented StackPanel, each bound to a particular property e.g.
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding HouseNumber}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Street}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding City}" />
</StackPanel>
The advantage of the second approach is that it gives you flexibility in the UI to change how addresses are displayed, e.g. multiple lines, formatting, etc.; the downside is that it gets complicated if you have conditional logic e.g. an optional flat number or second address line.
I tried to reproduce the problem and succeeded.
I activated the step-into-.NET debugging options, and saw that WPF does not listen to INotifyPropertyChanged if the path in the binding is empty.
What worked to get a change to be reflected in the list box is to replace the whole object in the ObservableCollection. This triggers the INotifyCollectionChanged, with the Replace action.
But this may not be acceptable in your case. And it could be seen more like a hack than a solid solution.
I'd seriously consider having a DataTemplate for Address. There you should bind to the exact properties you need (which would create the listener for INotifyPropertyChanged). It is more flexible than ToString() and you may encounter cases where you have a need for ToString() to do something for non-UI stuff, which would create a conflict. And honestly, ToString is not really meant for UI stuff.
DataForms seem to update their CurrentItem as soon as the user tabs out of a field. This happens even when AutoCommit = false. The side effect of that behavior is that other controls that are bound to the data update while the user edits data instead of when the user clicks Ok to accept the DataForm changes. Is there a way to modify that behavior to postpone writing data to CurrentItem to when the user accepts the changes?
EDIT: Here's most of the Xaml for a DataForm I'm using:
xmlns:DataFormControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
<DataFormControls:DataForm x:Name="dataForm" AutoCommit="False" AutoEdit="False">
<DataFormControls:DataForm.EditTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<DataFormControls:DataField Label="Title">
<TextBox Text="{Binding Title, Mode=TwoWay}" Style="{StaticResource TextBoxStyle}"/>
</DataFormControls:DataField>
<DataFormControls:DataField Label="First Name">
<TextBox Text="{Binding FirstName, Mode=TwoWay}"/>
</DataFormControls:DataField>
<DataFormControls:DataField Label="Middle Name">
<TextBox Text="{Binding MiddleName, Mode=TwoWay}"/>
</DataFormControls:DataField>
<DataFormControls:DataField Label="Last Name">
<TextBox Text="{Binding LastName, Mode=TwoWay}"/>
</DataFormControls:DataField>
</StackPanel>
</DataTemplate>
</DataFormControls:DataForm.EditTemplate>
</DataFormControls:DataForm>
EDIT 2: The workaround I'm using to avoid this behavior is to make a copy of the object to be edited and set it to the DataForm.CurrentItem property, then if the user accepts the edit the data is copied back to the original object. I'm hoping there's a better solution out there.
Everything is bound to the same instance of the entity. Since the DataForm fields are bound to properties on the entity, when you leave a field, it calls the property setter, changing the value and raising INotifyPropertyChanged.PropertyChanged events. This then informs other bound controls that the value has changed and their bindings are updated.
If you really want to prevent this, you would need to do some hefty work to clone the entity that the DataForm is bound to, and then when the item is committed, update the original with the clone's values. This would not be recommended.
Alternatively, you could have multiple instances of the DomainContext and load the entities twice--once for the DataForm and once for the other displays. After submitting changes on one, you could re-load the entities for the other. This will likely lead to other problems in your application though, so it's also not recommended.
I am curious why the live binding is causing a problem.
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.
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.