Binding two textBoxes using MVVM Light - c#

I have two text boxes side by side, InputInches and InputMillimeters what I want to do is inches to millimeters conversions similar to the way Goolge conversion works. What I want to happen is that when the user starts typing in the first text box InputInches the result would be shown in the second text box (InputMillimeters) and vice-versa, if the user starts typing in the second text box the result would be shown in the first text box.
The following code works fine to do inches to millimeters conversion exactly as I want but if I un-comment the code in the convertMillimetersToInches() method I get an error.
Any suggestion on how can I do this type of binding using MVVM Light?
UI:
XAML:
<TextBox x:Name="textBox1"
Text="{Binding InputInches,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="textBox2"
Text="{Binding InputMillimeters,UpdateSourceTrigger=PropertyChanged}"/>
ViewModel:
namespace MyApp.ViewModel
{
public class ConversionViewModel : ViewModelBase
{
private string _inputInches;
private string _inputInchesTrimmed;
private string _inputMillimeters;
private string _inputMillimetersTrimmed;
public ConversionViewModel()
{
}
public string InputInches
{
get { return _inputInches; }
set {
_inputInches = value;
_inputInchesTrimmed = value.Trim();
RaisePropertyChanged();
if (_inputInchesTrimmed == "") {
_inputInchesTrimmed = "0";
}
convertInchesToMillimeters();
}
}
public string InputMillimeters
{
get { return _inputMillimeters; }
set {
_inputMillimeters = value;
_inputMillimetersTrimmed = value.Trim();
RaisePropertyChanged();
if (_inputMillimetersTrimmed == "") {
_inputMillimetersTrimmed = "0";
}
convertMillimetersToInches();
}
}
/// CONVERSION METHODS
private void convertInchesToMillimeters()
{
double millimeters = Convert.ToDouble(_inputInchesTrimmed) * 25.4;
InputMillimeters = Convert.ToString(millimeters);
}
private void convertMillimetersToInches()
{
//double inches = Convert.ToDouble(_inputInchesTrimmed) / 25.4;
//InputInches = Convert.ToString(inches);
}
}
}
ERROR MESSAGE:
Make sure you don't have an infinite loop or inifinite recursion

Simple answer :
Check on your methods the equality before set value (duplicate for other method) :
private void convertInchesToMillimeters()
{
string millimeters = (Convert.ToDouble(_inputInchesTrimmed) * 25.4).ToString();
if(millimeters != InputMillimeters) InputMillimeters = millimeters;
}
More complex answer. Use just one property and implement two wpf converter(see https://www.wpf-tutorial.com/data-binding/value-conversion-with-ivalueconverter/)

Related

How to send an object of another class to itself?

I am very new to WPF and couldn't get my head around this so checked couple tutorials and kinda merged them together and I am here with this mess. Dont really know how should I describe my issue around so I decided to write here.
What I am working on;
Using a WPF I got a menu opening UserControls inside the Main Window.
When Main Window starts I am starting a thread like this so I get a loop
private void MainLoop()
{
Thread th = Thread.CurrentThread;
while (th.ThreadState != ThreadState.AbortRequested &&
th.ThreadState != ThreadState.Aborted)
{
if (bMainOk)
{
switch (activeUC)
{
case "ucDurum":
LoopControl.ucDurumLoop(plc, connectionString);
break;
case "ucAyarlar":
//LoopControl.ucAyarlarLoop();
break;
default:
break;
}
}
Thread.Sleep(2000);
}
}
That LoopControl class has functions inside that are looping depending on the active UserControl.
public class LoopControl : ObservableObject
{
public static void ucDurumLoop(ActUtlType plc, string connectionString)
{
DurumVM durum = new DurumVM();
// The values do change before this lines, I didnt put them for the sake of clarity
durum.RaporAktif = "true";
durum.RaporAdedi = arrayData[0].ToString();
durum.BufferPercent = "%" + (float)arrayData[0] / 250 * 100;
durum.PlcSonCounter = arrayData[4].ToString();
durum.SqlSonCounter = sonCntr.ToString();
}
}
Which is Connected to a Main ViewModel then sub ViewModels like so
class MainVM
{
public DurumVM Durum { get; private set; }
public AyarlarVM Ayarlar { get; private set; }
public MainVM()
{
Durum = new DurumVM();
Ayarlar = new AyarlarVM();
}
}
public class DurumVM : ObservableObject
{
#region PLCVars
private bool _raporAktif;
private string _raporAdedi;
private string _bufferPercent;
private string _plcSonCounter;
private string _sqlSonCounter;
#endregion
#region PLCGetSets
public string RaporAktif
{
get
{
if (_raporAktif)
return "Düzenlenecek True";
return "Düzenlenecek False";
}
set
{
_raporAktif = Convert.ToBoolean(value);
OnPropertyChanged("RaporAktif");
}
}
public string RaporAdedi
{
get
{
if (string.IsNullOrEmpty(_raporAdedi))
return "Zeroh";
return _raporAdedi;
}
set
{
_raporAdedi = value;
OnPropertyChanged("RaporAdedi");
}
}
public string BufferPercent
{
get
{
if (string.IsNullOrEmpty(_bufferPercent))
return "%0";
return _bufferPercent;
}
set
{
_bufferPercent = (value.Length <= 5 ? value : value.Substring(0, 5));
OnPropertyChanged("BufferPercent");
}
}
public string PlcSonCounter
{
get
{
if (string.IsNullOrEmpty(_plcSonCounter))
return "0";
return _plcSonCounter;
}
set
{
_plcSonCounter = value;
OnPropertyChanged("PlcSonCounter");
}
}
public string SqlSonCounter
{
get
{
if (string.IsNullOrEmpty(_sqlSonCounter))
return "0";
return _sqlSonCounter;
}
set
{
_sqlSonCounter = value;
OnPropertyChanged("SqlSonCounter");
}
}
#endregion
}
And the user control are bound to these values:
<StackPanel>
<DockPanel>
<Label Content="Aktif mi? :" VerticalAlignment="Center"/>
<TextBlock Text="{Binding RaporAktif, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" Margin="0,0,25,0" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock.Effect>
<DropShadowEffect/>
</TextBlock.Effect>
</TextBlock>
</DockPanel>
...
public partial class ucDurum : UserControl
{
MainVM viewModel = new MainVM();
//LoopControl viewModel = new LoopControl();
public ucDurum()
{
InitializeComponent();
this.DataContext = viewModel.Durum;
Control.activeUC = "ucDurum";
}
}
Quick sum:
I got set of user controls and one main loop. Main loop has parts that only works if correct user control is active, connects to somewhere else and gets values then sets these values on a reference, sends it to user control and textblocks the values bound will update
What is wrong that I know:
Practice might be very incorrect. It was looking good in my head but I realized the problem. Mainly due to my lack of C# knowledge. The main loop gets reference of VM sets the datas but never sends them anywhere as I bolded in quick summary. I certain the problem is there since the values I got are correct, when I change values view button etc they do change and update. But VM has no idea about LoopControl.
In MVC I'd return the referenced object then get values on view. In here the values are bound directly and I am kind of lost.
Thanks in advance.
Update:
I'm sorry I wasn't clear enough above the last paragraphs.
What I expect it to do:
LoopControl.ucDurumLoop updates DurumVM using referenced object so the user control "ucDurum" can see it and update TextBlock values accordingly.
What is happening:
I set up the referenced object in LoopControl.ucDurumLoop with the values but that is just an object and stays there. Doesn't go to DurumVM and update values using their Get/Set 's, so values are same and user control "ucDurum" has no idea what is going on on LoopControl.
Well I was being stupid.
Problem was, I was creating new object reference and updating it while I had to reference the one on UserControl.
All I needed was changing a line in LoopControl
public class LoopControl : ObservableObject
{
public static void ucDurumLoop(ActUtlType plc, string connectionString)
{
DurumVM durum = new DurumVM(); // Changing this
DurumVM durum = ucDurum.viewModel; // To this.
durum.RaporAktif = "true";
durum.RaporAdedi = arrayData[0].ToString();
durum.BufferPercent = "%" + (float)arrayData[0] / 250 * 100;
durum.PlcSonCounter = arrayData[4].ToString();
durum.SqlSonCounter = sonCntr.ToString();
}
}

Creating Replacable Placeholder Text in WPF ComboBox

Say I have a combobox, where the contents are going to be some function options that I'd like to be able to fill out with parameters. For instance, the options might be
[ComboBox option menu drops down to show the following text options]
Foo(int x)
Bar(int y, int z)
HelloWorld(string q)
When any of these options are selected by the user, the combobox will close the options menu (as normal) and show the selected option. However, I'd like the parameter portion to be a sort of 'pre-highlighted' block of text, which upon clicking gives you focus of that highlight so you can immediately over-write it with your parameter choice.
Cheap'n'cheeesy, and it doesn't dream of handling Bradley's case of multiple parameters: It just selects everything between the parens. But it handles the cases you listed.
In real life you'd want to set up this event with an attached behavior, of course.
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var cb = (ComboBox)sender;
var item = cb.SelectedItem as SelectMethodCallItem;
// This event is raised when user alters the text, but
// SelectedItem will be null in that case.
if (item != null && item.HasSelection)
{
var edit = (TextBox)cb.Template.FindName("PART_EditableTextBox", cb);
Action setsel = () =>
{
edit.SelectionStart = item.SelStart;
edit.SelectionLength = item.SelLEngth;
};
// the BeginInvoke/application idle gimmick is so it happens
// after this event is over with, so the change we make isn't stepped on
App.Current.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.ApplicationIdle, setsel);
}
}
In real life, you could do something much more clever and robust to identify the replaceable parameter text.
public class SelectMethodCallItem
{
public SelectMethodCallItem(String text)
{
Text = text;
SelStart = text.IndexOf('(');
SelEnd = text.IndexOf(')');
if (SelStart > -1 && SelEnd > -1)
{
++SelStart;
++SelEnd;
}
else
{
SelStart = SelEnd = -1;
}
}
public String Text { get; set; }
public int SelStart { get; private set; }
public int SelEnd { get; private set; }
public int SelLEngth => (SelEnd - SelStart) - 1;
public bool HasSelection => SelStart > -1 && SelEnd > -1;
}
XAML
<ComboBox
IsEditable="True"
ItemsSource="{Binding CBItems}"
SelectionChanged="ComboBox_SelectionChanged"
DisplayMemberPath="Text"
/>
Code behind
public Form()
{
InitializeComponent();
DataContext = new
{
CBItems = new[] {
new SelectMethodCallItem("sin(float x)"),
new SelectMethodCallItem("cos(float x)"),
new SelectMethodCallItem("foobar(string s)"),
}
};
}

Need to use created class to create an instance and use it in a Form.cs file

So, I'm at a loss here. Have a small project to work on. Have to create a trip class then create a Windows Form app and use the class I created to use the form to calculate miles per gallons used and Cost Per Mile.
Have completed the class:
namespace TripCalculator
{
class Trip
{
//Data members of class
private string destination;
private double distTrav;
private double totalCostGas;
private double numGallonsGas;
//Default Constructor
public Trip()
{
}
//Constructor with all parameters
public Trip(string tripDest, double milesTrav, double ttlPriceGas, n double numGalls)
{
destination = tripDest;
distTrav = milesTrav;
totalCostGas = ttlPriceGas;
numGallonsGas = numGalls;
}
//Propery for destination data field
public string Destination
{
set
{
destination = value;
}
get
{
return destination;
}
}
public double DistTrav
{
set
{
distTrav = value;
}
get
{
return distTrav;
}
}
public double TotalCostGas
{
set
{
totalCostGas = value;
}
get
{
return totalCostGas;
}
}
public double NumGallonsGas
{
set
{
numGallonsGas = value;
}
get
{
return numGallonsGas;
}
}
public double CalculateMPG()
{
return (distTrav / numGallonsGas);
}
public double CalculateCPM()
{
return (totalCostGas / numGallonsGas);
}
public override string ToString()
{
return CalculateMPG().ToString();
}
}
}
I want to be able to input destination, distance, cost, and gallons of gas into the form. Then I want the mpg and cost per mile to return to me in a textboxes.
Here's the form.cs
namespace TripCalculator
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void calcBtn_Click(object sender, EventArgs e)
{
string destination;
double distTrav;
double totalCostGas;
double numGallonsGas;
destBox.Focus();
distBox.Focus();
gasBox.Focus();
galBox.Focus();
Trip aTrip = new Trip (destination, distTrav, totalCostGas, numGallonsGas );
mpgTxt.Text = aTrip.CalculateMPG().ToString();
cpmTxt.Text = aTrip.CalculateCPM().ToString();
destBox.Enabled = false;
}
}
}
Im getting 4 errors saying "Use of unassigned local variable 'destination' (As well as for the other 3 variables above). It'll start the program, but returns nothing when I type in the text boxes and click the button. What am I doing wrong? Please help! Thanks.
private void calcBtn_Click(object sender, EventArgs e)
{
string destination; // Give value for these
double distTrav; // Give value for these
double totalCostGas; // Give value for these
double numGallonsGas; // Give value for these
}
Assign some value, either from user input or hard coded value for testing
Something like the code below, where you get the values from the text box in your Winform assuming you have such text boxes.
string destination = DestinationTextBox.Text;
double distTrav = double.Parse(DistTravTextBox.Text);
double totalCostGas = double.Parse(TotalCostGasTextBox.Text);
double numGallonsGas = double.Parse(NumGallonsGasTextBox.Text);
You need to set the textbox values for the variables
string destination = txtDestination.Text;
double distTrav = double.Parse(txtTrav.Text);
double totalCostGas = double.Parse(txtCostGas.Text);
double numGallonsGas = double.Parse(GallonsGas.Text);

How to handle making a Window in WPF (C#) that takes parameters?

I am using WPF (C#) for the first time and this I've encountered my first "real" design choice. I have a main window and when the user enters some data and presses the "plot" Button, a new window will come up showing a graph.
This graph window I am defining myself with a combination of xaml and the code-behind file. The issue is that 2 parameters this window has is the x axis title and the y axis title. So, these should be "parameters" to making this window.
I am confused by this because I'm using MVVM and I have a "ViewModel" for the window called GraphWindowPresenter and a "View" for the class called GraphWindowView.
At first, I tried to have an xAxis property and a yAxis property in my GraphWindowPresenter but that will not work since I need to "bind" to these values upon construction of the GraphWindowView. Additionally, this approach would require that my GraphWindowPresenter take an xAxis parameter and a yAxis parameter which is problamatic as well since I just create an instance of the class in the xaml of GraphWindowView.
I'm thinking of a possible soltuion that I can just have my GraphWindowView take the xAxis and yAxis parameters but doesn't this violate MVVM? I would rather not do that.
Note: This is similar to this post MVVM: Binding a ViewModel which takes constructor args to a UserControl. But in my scenario it is tricky since I have a parent window and a pop up child window.
Question: What is the best approach to this design issue? What are the "best practices" regarding this scenario?
Possible Answer:
Is this the correct use of dependency properties that you described? Is this a "clean" solution?
private void doGraph()
{
if (log == null) // if a log is not loaded
{
MessageBoxResult mbr = MessageBox.Show("A log file must be " +
"loaded before plotting.",
"Warning",
MessageBoxButton.OK,
MessageBoxImage.Exclamation);
return;
}
// NOW MUST PRESENT GRAPH WINDOW
GraphWindowView gwv = new GraphWindowView();
gwv.xAxis = X_AXIS_VALUE:
gwv.yAxis = Y_AXIS_VALUE;
gwv.Show();
}
And in my GraphWindowView class I have the code:
public partial class GraphWindowView : Window
{
// Using a DependencyProperty as the backing store for yAxis.
public static readonly DependencyProperty yAxisProperty =
DependencyProperty.Register("yAxis", typeof(string), typeof(GraphWindowView));
// Using a DependencyProperty as the backing store for xAxis.
public static readonly DependencyProperty xAxisProperty =
DependencyProperty.Register("xAxis", typeof(string), typeof(GraphWindowView));
public string xAxis
{
get { return (string)GetValue(xAxisProperty); }
set { SetValue(xAxisProperty, value); }
}
public string yAxis
{
get { return (string)GetValue(yAxisProperty); }
set { SetValue(yAxisProperty, value); }
}
public GraphWindowView()
{
InitializeComponent();
}
}
You can you userSetting properties
One my application have same scenario in that i have mainWindow that accept HostAddress,Port value and it will use another window when i click connect so i am using userSetting properties. I am also using MVVM pattern check code snippet below
XAML:
<TextBox Width="120" Canvas.Left="132" Canvas.Top="16" Text="{Binding Path=Server,Mode=TwoWay}"/>
<TextBox Width="120" Canvas.Left="132" Canvas.Top="42" Text="{Binding Path=DisplayPort,Mode=TwoWay}"/>
<TextBox Width="120" Canvas.Left="132" Canvas.Top="69" Text="{Binding Path=CtrlPort,Mode=TwoWay}"/>
<Button Content="Launch" Name="btnLaunch" Command="{Binding Path=appSetting}" Canvas.Left="132" Canvas.Top="100" Width="120" Height="51" Click="btnLaunch_Click" />
VIEWMODE:
public class SettingsViewModel : ViewModelBase
{
private Settings _settings { get; set; }
public SettingsViewModel()
{
appSetting = new RelayCommand(this.AppSettingsCommand);
_settings = ApplicationTest.Properties.Settings.Default;
}
private string _server = Settings.Default.Server;
public string Server
{
get { return this._server; }
set
{
if (this._server != value)
{
this._server = value;
OnPropertyChanged("Server");
}
}
}
private string _displayPort = Settings.Default.DisplayPort;
public string DisplayPort
{
get { return this._displayPort; }
set
{
if (this._displayPort != value)
{
this._displayPort = value;
OnPropertyChanged("DisplayPort");
}
}
}
private string _ctrlPort = Settings.Default.CtrlPort;
public string CtrlPort
{
get { return this._ctrlPort; }
set
{
if (this._ctrlPort != value)
{
this._ctrlPort = value;
OnPropertyChanged("DisplayPort");
}
}
}
public RelayCommand appSetting
{
get;
set;
}
private void AppSettingsCommand()
{
this._settings.Server = this.Server;
this._settings.DisplayPort = this.DisplayPort;
this._settings.CtrlPort = this.CtrlPort;
this._settings.Save();
}

Accessing Text Box Values from Form to another class

I have a WPF Application which contains a class called RateView.xaml.cs and MainWindow.xaml.cs
The MainWindow.xaml.cs contains three textboxes of which values I want to pass into the RateView.xaml.cs. The content of these textboxes can be changed by the end user but regardless of that I always want whatever the value is of the textbox to be going into rateview.xaml.cs.
How can this be done?
I am a newbie to coding hence not sure, someone mentioned Get and Set statements, if so how can I do these?
Currently I access my textboxes like this in the MainWindow:
private float GetSomeNumber()
{
bool Number1 = false;
float parsedNumber1Value = 0.00F;
Number1 = float.TryParse(Number1_TextBox.Text, out parsedNumber1Value);
return parsedNumber1Value;
}
The GetSomeNumber() method is then passed to another seperate class to do some calculation with.
On intital load it works of the value from my method, but once someone changes the value rateview.xaml.cs doesn't recognise this change and always uses the values that were first loaded.
Thanks
Just a small example (This is winforms)
This is the mainwindow, where your textbox is:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
1
public string TextBox1Text
{
get { return textBox1.Text; }
set { textBox1.Text = value;
}
}
and this is a class where you want to interact with the textboxes:
public class Test
{
public Test(Form1 form)
{
//Set the text of the textbox in the form1
form.TextBox1Text = "Hello World";
}
}
To get and set the value of a textbox within another class/form you can do it with something like:
public string TextBox1Text
{ get { return textBox1.Text; }
set { textBox1.Text = value; } }

Categories

Resources