C#/WPF Retrieve textbox input value - c#

In my app, I am trying to get the input value of a textbox for use later but I'm unable to successfully get the value. Any ideas where I have made a mistake or how to fix it? Below is my code, if more code is needed, feel free to ask. Thank you in advance.
View Model:
namespace Creator.ViewModels
{
public class CreatureCreatorViewModel : ViewModelBase, INotifyPropertyChanged
{
public CreatureModel CreateNewCreature { get; set; }
/*Variables to "get" input data*/
private string creatureName;
public string CreatureName
{
get {
return creatureName;
}
set
{
if(!string.Equals(creatureName, value))
{
creatureName = value;
OnPropertyChanged("CreatureName");
}
}
}
public CreatureCreatorViewModel(NavigationStore navigationStore)
{
CreateNewCreature = new CreatureModel().NewCreature(creatureName);
}
}
}
View:
<TextBox x:Name="CreatureNameBox" Text="{Binding Path=CreatureName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" TextWrapping="NoWrap" FontFamily="Century Gothic" FontSize="16" Margin="150,0,150,16" MaxLines="1" Height="26" HorizontalAlignment="Stretch" VerticalAlignment="Center" Padding="5,3,5,3" MaxLength="100"/>
ViewModelBase:
namespace Creator.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public virtual void Dispose() { }
}
}

I did a simple test using the source code you provided. And I could see that it's normal as shown in the image below.
I put the sample on GitHub to compare with your source code.
Here
<TextBox Text="{Binding Path=CreatureName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="{Binding CreatureName}"/>

Related

Problem with adding text from Textbox_Scan to TextBox_SerialBuffer with ENTER (MVVM)

I have a problem with adding inputed text from TextBox_Scan to TextBox_SerialBuffer with key ENTER.
My code is not working correctly , issue is that text is copy on TextBox_SerialBuffer when i have some event , but i need to add when use key enter.
Add Class ViewModel in folder ViewsMoldel
<Window.DataContext>
<Viewsmodel:ViewModel/>
</Window.DataContext>
TextBox_Scan XAML :
<TextBox x:Name="TextBox_Scan"
HorizontalAlignment="Left"
Margin="173,165,0,0"
TextWrapping="NoWrap"
VerticalAlignment="Top"
Width="302"
FontSize="13"
KeyDown="TextBox_Scan_KeyDown"
FontFamily="Consolas"
FontWeight="Normal"
Text="{Binding Textadding, Mode=TwoWay}"
TextBox_SerialBuffer XAML :
<TextBox x:Name ="TextBox_SerialBuffer"
TextWrapping="Wrap"
TextAlignment="Justify"
Margin="1,1,1,1"
Background="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"
Padding="1"
FontFamily="Consolas"
FontSize="13"
Text="{Binding Textadding, Mode=TwoWay}"
Class ViewModel :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace New_Takaya.ViewsModel
{
public class ViewModel : INotifyPropertyChanged
{
private string _Text = "";
public event PropertyChangedEventHandler PropertyChanged;
public string Textadding
{
get { return _Text; }
set
{
_Text = value;
OnPropertyChanged("Textadding");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Help me please to solve this problem .
Thank you!
You bind the same Property Textadding (why not Text...?) to both TextBoxes. Therefore if you enter text into TextBox_Scan it immediatelly updates TextBox_SerialBuffer
use two separate Properties, define an inputbinding on your Textbox and Copy the Value in a command.
Something like:
<TextBox Text="{Binding Input, UpdateSourceTrigger=PropertyChanged }">
<TextBox.InputBindings>
<KeyBinding Key="Return"
CommandParameter="{Binding}"
Command="{Binding CopyValueCommand}" />
</TextBox.InputBindings>
</TextBox>
<TextBox Text="{Binding Output}" />
and corresponding ViewModel (I prefer PropertyChanged.Fody over manually implement INotifyPropertyChanged)
[PropertyChanged.AddINotifyPropertyChangedInterface]
class ViewModel4
{
public ViewModel4()
{
CopyValueCommand = new CopyCommand();
}
public string Input { get; set; }
public string Output { get; set; }
public ICommand CopyValueCommand { get; }
}
class CopyCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (parameter is ViewModel4 data)
{
data.Output = data.Input;
}
}
}

Issue Saving Model from ViewMode/Viewl MVVM

So what I want is when SelectedModel.TechName is updated that it physically saves to the model so that as long as the application is running it will remain whatever the user enters.
I have 2 views SelectedModel.TechName is called in both views. It pulls the data from the model however when I change views the data resets.
Any Suggestion?
edit: I am trying to make the data entered persistent, I thought setting the value would do this however every time i change between views it resets the data. In fact it blinks the data then resets it.
Field from DefaultView.Xaml
<StackPanel Grid.Row="0" Grid.Column="6" Grid.ColumnSpan="1" Margin="5 5 5 0">
<TextBox Name="techName" Text="{Binding SelectedModel.TechName,Mode=TwoWay}" BorderBrush="#FF4A5780" Grid.RowSpan="2"/>
</StackPanel>
<TextBlock x:Name="TextUpdate" Grid.Column="5" HorizontalAlignment="Left" Margin="41,0,0,0"
Grid.Row="1" Text="{Binding SelectedModel.TechName}" TextWrapping="Wrap" VerticalAlignment="Center"/>
DataModel.cs Model File
namespace callFlow.Models
{
public class DataModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string techName;
public DataModel()
{
}
public string TechName
{
get { return techName; }
set { techName = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string techName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(techName));
}
}
}
DefaultViewModel.cs
namespace callFlow.ViewModels
{
public class DefaultViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public DefaultViewModel() { }
private ObservableCollection<DataModel> model = new ObservableCollection<DataModel>();
private DataModel selectedModel;
private DataModel _SelectedModel;
public DataModel SelectedModel
{
get { return _SelectedModel ?? (_SelectedModel = new SelectedModel()); }
set { _SelectedModel = value;
OnPropertyChanged(); }
}
public void changeSelectedModel(DataModel newSelectedModel)
{
SelectedModel.TechName = newSelectedModel.TechName;
}
private void OnPropertyChanged([CallerMemberName] string techNameVM = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(techNameVM));
}
}
}
On your binding you have
UpdateSourceTrigger=Explicit
in
Text="{Binding SelectedModel.TechName,Mode=TwoWay,
UpdateSourceTrigger=Explicit}"
When you do that, you have to write code to update the source property. Which is the viewmodel property.
Since you don't do that, the viewmodel will not get updated when you type text in there.
You should either remove that off the binding or write some more code.
There are multiple potential issues in your code. First, you use Explicit as UpdateSourceTrigger, but you never call UpdateSource, at least you do not show that in your code. Consequently, the property will never be updated. Use PropertyChanged or LostFocus instead.
If you set the UpdateSourceTrigger value to Explicit, you must call the UpdateSource method or the changes will not propagate back to the source.
Furthermore, you implement INotifyPropertyChanged in your view models, but you never call OnPropertyChanged. Hence, bindings will never be updated when a property changes its value. Your properties should look like below. This applies to all properties that you expose.
public string TechName
{
get { return techName; }
set
{
if (techName != value)
{
techName = value;
OnPropertyChanged();
}
}
}
It is not clear how you create your views and set their DataContext. If you create the data context view model in the XAML of your view, it will be created each time you instantiate a new view.
Simple solution
Remove the UpdateSourceTriger=Explicit from your DefaultView.xaml
<TextBox Name="techName" Text="{Binding SelectedModel.TechName,Mode=TwoWay}" BorderBrush="#FF4A5780" Grid.RowSpan="2"/>
Call the OnPropertyChanged method in the DataModel.TechName's setter. Like this:
public string TechName
{
get {
return techName;
}
set {
techName = value;
OnPropertyChanged();
}
}
Better solution
There are a few problems with your code. Here's how to fix them:
DefaultView.xaml
Remove the UpdateSourceTrigger=Explicit. It requires you to update the binding manually (from code) and you're not doing that.
<StackPanel Grid.Row="0" Grid.Column="6" Grid.ColumnSpan="1" Margin="5 5 5 0">
<TextBox Name="techName" Text="{Binding SelectedModel.TechName,Mode=TwoWay}" BorderBrush="#FF4A5780" Grid.RowSpan="2"/>
<TextBlock x:Name="TextUpdate" Grid.Column="5" HorizontalAlignment="Left" Margin="41,0,0,0"
Grid.Row="1" Text="{Binding SelectedModel.TechName}" TextWrapping="Wrap" VerticalAlignment="Center"/>
DataModel.cs
You were not calling the OnPropertyChanged method in TechName's setter, that's why it wasn't updating. I've done that and refactored the code a bit
public class DataModel : ObservableObject
{
private string _techName;
public string TechName
{
get => _techName;
set {
_techName = value;
OnPropertyChanged();
}
}
}
DefaultViewModel.cs
Here I've just removed the empty default constructor, the extra private DataModel field and refactored the code.
public class DefaultViewModel : ObservableObject
{
private ObservableCollection<DataModel> Models = new ObservableCollection<DataModel>();
private DataModel _selectedModel;
public DataModel SelectedModel
{
get => _selectedModel ?? (_selectedModel = new SelectedModel());
set {
_selectedModel = value;
OnPropertyChanged();
}
}
}
INotifyPropertyChanged implementation - ObservableObject.cs
I've added this class to simplify the rest of the code, since you were using the same code in both DataModel.cs and DefaultViewModel.cs
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Clearing textboxes

I'm making an input page and I'm trying to implement a reset button. After a click on the button, the UI should be empty again.
I thought that entering an empty string would deal with this. In the code it seems to work and the value does get changed to "" but in the UI the typed text stays visible (so it doesn't show the empty "" string). I also tried with string.Empty as suggested in here but that also doesn't seem to work.
Am I missing something here? I'm kinda new to programming so if I did something horribly wrong, don't laugh too hard ;)
I'm using an MVVM pattern and Fody Weaver to deal with the property changed part of the code.
The UI / XAML
<TextBlock Text="Naam:"
Grid.Column="0"
Style="{StaticResource InputInputBlock}"
/>
<TextBox Foreground="White"
Grid.Column="1"
Text="{Binding Name, Mode=TwoWay}"
Style="{StaticResource InputInputBox}"
/>
<Button Content="Reset"
Height="50"
Width="150"
Grid.Column="0"
Grid.Row="2"
VerticalAlignment="Top"
HorizontalAlignment="Center"
Style="{StaticResource FlatButton}"
Command="{Binding ResetCommand}"
/>
The view model
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
}
}
public AddStakeholderViewModel()
{
ResetCommand = new RelayCommand(() => ResetForm());
}
private void ResetForm()
{
Name = " ";
}
You can implement the INotifyPropertyChanged interface in your class. This works for me:
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name");
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
XAML:
<TextBox Foreground="White"
Grid.Column="1"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource InputInputBox}"
/>
MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = newPerson;
}
Person newPerson = new Person();
private void button_Click(object sender, RoutedEventArgs e)
{
newPerson.Name = "";
}
}

Refresh View When Binding Object Changes

I have a Textbox in WPF which has its "Text" Property bound to a string "EmployeeSource.ID" with Mode=TwoWay. My problem is that when i change the EmployeeSource object, the binding does not work. What is wrong in my approach?
XAML
<TextBox x:Name="NameTextBox" Margin="5,5,10,5" TextWrapping="Wrap"
Text="{Binding SelectedEmployee.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1" Grid.Column="1" />
Code Behind
private Employee _selectedEmployee;
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
UpdateTextBoxes();
}
}
private void UpdateTextBoxes()
{
NameTextBox.Text = SelectedEmployee?.Name;
}
Please try the code below. You need to implement the INotifyPropertyChanged interface inorder to achieve data binding in WPF. This is the basic concept of WPF data binding and MVVM pattern. This should work for you.
Code behind:
public class YourClassName : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Employee _selectedEmployee;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
// The constructor is private to enforce the factory pattern.
private YourClassName()
{
_selectedEmployee = new Employee();
}
public Employee selectedEmployee
{
get
{
return this._selectedEmployee;
}
set
{
if (value != this._selectedEmployee)
{
this._selectedEmployee = value;
NotifyPropertyChanged("selectedEmployee");
}
}
}
}
XAML :
<TextBox x:Name="NameTextBox" Margin="5,5,10,5" TextWrapping="Wrap"
Text="{Binding selectedEmployee.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1" Grid.Column="1" />

Bind INotifyPropertyChanged class as Property in another class

I have a class that has a fairly long running process that I want the GUI to give progress on.
The class has a property called Progress that is a class implementing INotifyPropertyChanged. I'm using a BusyWorker and bind the class's Progress property to it's datacontext, but whenever the progress changes the BusyWorker does not show anything. I don't know if I'm making any sense here, so here's some code:
The class in question:
public class MyClass
{
public Progress MyProgress { get; set; }
public void Run()
{
MyProgress = new Progress();
MyProgress.Status = "Initialising";
// Do stuff, update progress, etc.
}
}
public class Progress : INotifyPropertyChanged
{
private string status;
public string Status
{
get { return status; }
set
{
status = value;
OnPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
XAML:
// ...
<xctk:BusyIndicator HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="busyIndicator" VerticalAlignment="Stretch" BusyContent="{Binding}">
<xctk:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="{Binding Status}" FontWeight="Bold" HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
</xctk:BusyIndicator.BusyContentTemplate>
</xctk:BusyIndicator>
// ...
XAML.CS:
MyClass test = new MyClass();
BusyIndicator.DataContext = test.MyProgress;
BusyIndicator.IsBusy = true;
test.Run();
If I run it like this, and stop at the OnPropertyChanged call, PropertChanged is always null. If I make a separate Progress object in my xaml.cs it works just fine, but I want my 'Run' method to handle this. Is this even possible?
The Problem is you are assigning Data Context before calling the run method which means by the time you are assigning data context "MyProgress" Object is "Null".. so data context is null before calling the "Run" method.. you are calling the Run method which creates an instance for "MyProgress" but since your "MyClass" is not "INotifyPropertyChanged" its not able to notify the data context change...
Solution is: Try creating MyProgress instance in the constructor of MyClass.. so by the time of assigning data context will not be null and the in the run method don't create any instance just update the status property..
Something like this
public class MyClass
{
public Progress MyProgress { get; set; }
public MyClass()
{
MyProgress = new Progress();
}
public void Run()
{
MyProgress.Status = "Initialising";
// Do stuff, update progress, etc.
}
}
<xctk:BusyIndicator HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="busyIndicator" VerticalAlignment="Stretch" BusyContent="{Binding}">
<xctk:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="{Binding Path=Status}" FontWeight="Bold" HorizontalAlignment="Left" DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=BusyIndicator}}"/>
</StackPanel>
</DataTemplate>
</xctk:BusyIndicator.BusyContentTemplate>
// ...
This works for me:
*.xaml
<xctk:BusyIndicator HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="busyIndicator" VerticalAlignment="Stretch" IsBusy="True">
<xctk:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="{Binding Path=Test.MyProgress.Status, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" FontWeight="Bold" HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
</xctk:BusyIndicator.BusyContentTemplate>
</xctk:BusyIndicator>
*.xaml.cs:
public partial class MainWindow : Window
{
public MyClass Test { get; set; }
public MainWindow()
{
Test = new MyClass();
InitializeComponent();
Test.Run();
}
}
public class MyClass
{
public Progress MyProgress { get; set; }
public void Run()
{
MyProgress = new Progress();
MyProgress.Status = "Initialising";
// Do stuff, update progress, etc.
}
}
public class Progress : INotifyPropertyChanged
{
private string status;
public string Status
{
get { return status; }
set
{
status = value;
OnPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}

Categories

Resources