I have on the server side class Person with some properties, one of them is PhoneNumber (this is edmx auto generated - from DB). In the UI I have 2 textboxes, one is prefix, and the other is postfix, so I wanted to extend Person class and make it partial with 2 more properties, prefix and postfix and in the UI bind them to this properties which will update the PhoneNumber property, but somehow, prefix and postfix has value but PhoneNumber does not.
Here is my code:
Server Side:
Public partial class Person : EntityObject
{
//Auto generated from edmx
...
...
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public global::System.String PhoneNumber
{
get
{
return _PhoneNumber;
}
set
{
OnPhoneNumberChanging(value);
ReportPropertyChanging("PhoneNumber");
_PhoneNumber = StructuralObject.SetValidValue(value, true, "PhoneNumber");
ReportPropertyChanged("PhoneNumber");
OnPhoneNumberChanged();
}
}
}
public partial class Person
{
[DataMember]
public string PhoneNumberPrefix
{
get { return PhoneNumber.Substring(0, 3); }
set { PhoneNumber = value + PhoneNumber.Substring(3); }
}
[DataMember]
public string PhoneNumberPostfix
{
get { return PhoneNumber.Substring(3); }
set { PhoneNumber = PhoneNumber.Substring(0, 3) + value; }
}
}
Client Side:
DataContext is the Person.
<TextBox Grid.Column="0" MaxLength="3"
Text="{Binding PhoneNumberPrefix, Mode=TwoWay}" />
<TextBlock Grid.Column="1" Text="-" />
<TextBox Grid.Column="2"
Text="{Binding PhoneNumberPostfix, Mode=TwoWay}" />
Final Result:
After I add values to those textboxes I get the values in Prefix and Postfix but the PhoneNumber is still null. Print screen in debug mode:
I have tried to add UpdateSourceTrigger or Implement INotifyPropertyChanged and no help, can someone help me please?
Thanks in advance !
Instead of PhoneNumber.Substring(0, 3) try this:
PhoneNumber != null ? PhoneNumber.Substring(0, 3) : string.Empty;
Or ask manually if(PhoneNumber != null)...
Related
I would like to publish some information in a two-dimension data-grid. First column the name of the attribute, second the current value. Currently I created an array that I attached to the WPF data-grid and filled it upside down with an Attribute = "Username" and a Value = "Waldow". Now I would like to know if there is maybe another way by using a model class where I define every attribute as a string but in the end can display it in the same way, but have a better code.
Let`s say this is my model:
public class InformationModel
{
[Description("Hostname_Description")]
[Display(Name = "Hostname_Display")]
public string Hostname { get; set; }
[Display(Name = "Username_Display")]
public string Username { get; set; }
...... more values
}
Now I want a Datagrid like this:
Hostname | PC0004
Username | Waldow
but by just specifying the value:
<DataGrid x:Name="datagrid_information" ItemsSource="{Binding Information}" IsReadOnly="True" AutoGenerateColumns="False" Margin="11,10,10,0" HorizontalAlignment="Left" VerticalAlignment="Top" SelectionMode="Single" AlternationCount="1">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Display}" Header="Attribute"/>
<DataGridTextColumn Binding="{Binding Description}" Header="Value"/>
</DataGrid.Columns>
</DataGrid>
The model class will not be extended, meaning the entries will always be the same and no more rows will be added.
You should transform your model into a view model that contains a property per column that you want to display in the DataGrid (and bind to an IEnumerable<InformationViewModel>):
public class InformationModel
{
public string Attribute { get; set; }
public string Value { get; set; }
}
You can then set the properties based on the attributes using reflection.
There is no way to retrieve the value of the attributes in XAML though. You can only bind to public properties. It's then up to you as a developer to set these properties.
You might set the Attribute property of the sample class above to the string "Hostname", or to the value of the attribute that you can get using reflection. How the value is set doesn't matter as far as the XAML/UI is concerned.
As suggested I used an own class. So basically in my main class I set a listener on my informationModel and always when a value got changed it will launch the method Model_PropertyChanged. My InformationModel got extended by INotifyPropertyChanged, which calls OnPropertyChanged always when a value got changed:
public MainViewModel() {
// set listener
informationmodel.PropertyChanged += Model_PropertyChanged;
}
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Property changed: " + e.PropertyName);
// Update object in list
var obj = Informationdgm.FirstOrDefault(x => x.Attribute == informationmodel.GetType().GetProperty(e.PropertyName).GetCustomAttribute<DisplayAttribute>()?.Name.ToString());
if (obj != null)
{
obj.Value = informationmodel.GetType().GetProperty(e.PropertyName).GetValue(informationmodel, null);
}
}
I've a Model and I receive a property called birth in this format
birthday, birthplace
I've set this inside the Model
private string _birth;
/// <summary>
/// Birth information in format: birthday, birthplace
/// </summary>
[Column("birth")]
[JsonProperty("birth")]
public string Birth
{
get { return this._birth; }
set
{
this._birth= value;
OnPropertyChanged();
OnPropertyChanged("BirthData");
}
}
Also I've set a public modifier like this
/// <summary>
/// Split the birth data if exists
/// </summary>
public string[] BirthData
{
get { return this.Birth?.Split(','); }
}
In my XAML file I've setup everything correctly for my Model, except that I can't understand how I can use TwoWay binding on something like this. Below you will find the extract of the XAML for this particular property
<Label x:Name="BirthdayLabel" Content="Nato Il" />
<DatePicker x:Name="BirthdayDateTimePicker" SelectedDate="{Binding selectedModel.BirthData[0], Mode=TwoWay}"></DatePicker>
<Label x:Name="BirthplaceLabel" Content="Nato A"/>
<TextBox x:Name="BirthplaceTextBox" Text="{Binding selectedModel.BirthData[1], Mode=TwoWay}"/>
Of course this is not working properly because I'll ended up with selectedModel using the old information about the property even though the binding works as expected, I think that the TwoWay binding doesn't work with an Array of data like that.
I cannot change the DataSource and I've to find a method to use one textbox and one date picker and resemble together inside my ViewModel, which doesn't have much except a single method called Update() which take the current selectedModel.
ViewModel is about preparing the data for the View
So I suggest that you split and parse the data in the View Model and expose two properties for BirthDate and BirthPlace :
class Person
{
private string _birth;
public string Birth{
get { return this._birth; }
set
{
this._birth = value;
SplitBirthIntoBirthDayAndBirthPlace();
OnPropertyChanged();
}
}
private DateTime _birthday;
public DateTime Birthday{
get { return _birthday; }
set
{
_birthday = value;
ReComputeBirth();
OnPropertyChanged();
}
}
// Same for Birthplace ...
private void ReComputeBirth(){
// ... Format the data as expected ...
_birth = _birthday + ", " + _birthplace;
}
private void SplitBirthIntoBirthDayAndBirthPlace()
{
String[] values = _birth.Split(',', ' ');
// ... really make the parse here to fill _birthplace and _birthdate...
}
// ....
}
And the binding is simpler:
<DatePicker x:Name="BirthdayDateTimePicker"
SelectedDate="{Binding selectedModel.Birthday, Mode=TwoWay}"/>
<TextBox x:Name="BirthplaceTextBox"
Text="{Binding selectedModel.Birthplace, Mode=TwoWay}"/>
I have a class, derived from BindableBase, which contains two properties:
private string sourceCallNumber;
public string SourceCallNumber
{
get { return sourceCallNumber; }
set { SetProperty(ref sourceCallNumber, value); }
}
private string sourceMediaType;
public string SourceMediaType
{
get { return sourceMediaType; }
set { SetProperty(ref sourceMediaType, value); }
}
I have an ObservableCollection that contains a number of items using that class.
I have a GridView for which I set the ItemsSource to point to the ObservableCollection.
My problem is that if I change the value of, say, SourceMediaType on one of the items, the display does not update. I have put debugging in and can confirm that changing the value causes OnPropertyChanged to fire for that property.
I've read quite a few SO questions and answers around similar problems and I'm getting quite confused as to what I need to do in order to get this to work.
My understanding was that although ObservableCollection itself doesn't do anything when a property is changed, if the item itself triggers an OnPropertyChanged, that should get the display to update.
(There was one answer I read that proposed the use of code provided called TrulyObservableCollection but the problem I've got there is that everything refreshes rather than just the one item that has been updated).
What am I missing or misunderstanding, please?
Thanks.
C# apps should implement INotifyCollectionChanged and System.Collections.IList (not IList Of T).
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("Willa", "Cather"));
Add(new PersonName("Isak", "Dinesen"));
Add(new PersonName("Victor", "Hugo"));
Add(new PersonName("Jules", "Verne"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
firstName = first;
lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
Look at GridView.
#RodrigoSilva put me on the correct path ... the XAML that references the values was this:
<StackPanel>
<TextBlock Text="{Binding DisplayCallNumber}" Style="{StaticResource TitleTextBlockStyle}" Visibility="{Binding GotCallNumber, Converter={StaticResource DisplayIfTrue}}" Margin="0,0,0,10"/>
<TextBlock Text="{Binding DisplayMediaType}" Style="{StaticResource ItemTextStyle}" Visibility="{Binding GotMediaType, Converter={StaticResource DisplayIfTrue}}" Margin="0,0,0,10"/>
</StackPanel>
which doesn't directly reference the underlying properties SourceCallNumber and SourceMediaType. As a result, although OnPropertyChanged is correctly firing for SourceCallNumber and SourceMediaType, that isn't causing the display to update because that isn't what the XAML is pointing at.
Explicitly changing the call to SetProperty to this:
SetProperty(ref sourceCallNumber, value, "DisplayCallNumber");
fixes the problem but is not a GOOD fix because some other part of the app may actually be binding to SourceCallNumber and won't get a property update after this change. The GOOD fix is to use a converter as explained in http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464965.aspx.
I've a problem to use binding in a ComboBox.
<ComboBox
Margin="2"
x:Name="itemSelector"
SelectionChanged="itemSelector_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Id}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
My object is public class MyButton : MyElement and the Id attribute is set in MyElement class.
Of course Id is a public attribute: public string Id;.
When I try to access a attribute which is in the MyButton class it works but with the "Id" field I got nothing...
You can't bind to a field; you need to make Id a property instead.
Replace your field with public string Id { get; set; }
It should be property (with getter and setter), not field. Because you should notify the UI that the value of the property changed (and you should implement the INotifyPropertyChanged interface)
The code shold look like for C# 5
public string Id
{
get { return _id; }
set { SetProperty(ref _id, value); }
}
private string _id;
or for C# 4
public string Id
{
get { return _id; }
set
{
_id = value;
RaisePropertyChanged(() => Id);
}
}
private DateTime _id;
the full code you can see e.g. in this blog post (both for 4 and 5 version of C# language) http://jesseliberty.com/2012/06/28/c-5making-inotifypropertychanged-easier/
(Beware that the C# 5 requres .Net 4.5, therefore your application will not run on WinXP. C# 4 requires .Net4.0 so it has not this limitation.)
I have a ComboBox that it looks like this:
<ComboBox
ItemsSource="{Binding JobList}"
SelectedValue="{Binding Job,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
DisplayMemberPath="Title"
SelectedValuePath="Id"
IsEditable="True"
StaysOpenOnEdit="True"
/>
and its binding to my ViewModel that looks like this one:
public class ViewModel {
// this will fill from a database record for a person
public Job Job {
get { return _job; }
set {
if(value == _job) return;
_job = value;
OnPropertyChanged( () => Job );
}
}
// this will fill from all jobs records in database
public ObservableCollection<Job> JobList
{ /* do same as Job to implementing INotifyPropertyChanged */ }
}
and the Job is:
public class Job {
public int Id { get; set; }
public string Title { get; set; }
}
Really, I want to fill the ComboBox with job's list. So if the user's specified Job was in list, user can select it from list, otherwise, he enter a new Job.Title in ComboBox, the view model notify on it, and create a new Job item and also add it to JobList.
Have you any idea? can you help me please?
Create a string property in the viewModel something like 'SelectedJobName'
Bind this property to Combobox.Text
Wherever you want to use the entered value (Command, Presenter), check if selected value is not null and selectedJobName property value is not/matching.