Get TextBox name from WPF application with MVVM model in helper class - c#

So I am trying to build a MVVM application with WPF and i am stuck with this problem:
In the view class, I have a list of textBoxes that I want to acces in order to validate the inputs.
So I managed to do this by writing in the view class, and it works:
var list = mainGrid.Children.OfType<TextBox>().ToList();
var dictOfTb = new Dictionary<string, string>();
foreach (var item in list)
{
dictOfTb.Add(item.Name, item.Text);
}
But the problem is that I am trying to respect the MVVM pattern and I should to this in a helper class, lets call it ModelsPageHelper, and access it from the View, because here what I am trying to do is to only get data from the UI and pass it to the viewModel to get a result, and then to display it.
So in this class I wrote a method,GetValuesFromTextBoxes(List<TextBox> textBoxes) and I am writing the same code, but now I get a message saying that TextBox does not contain a definition for Name.
So the question is, how can I do the same thing in the helper class to acces the names of those textBoxes?

To do MVVM properly you have to stop thinking about code to validate text boxes, and think about a ViewModel that validates itself, and a view that displays the state of the ViewModel.
For example, if you have a property called Foo which must have the value Bar, the code would look like this:
public string FooError { get; private set; }
private string foo;
public string Foo
{
get => return foo;
set
{
foo = value;
if (foo == "Bar") FooError = "";
else FooError = "Foo must be Bar";
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(FooError));
}
}
and your XAML would look something like this:
<TextBox Text={Binding Foo}/>
<TextBlock Text={Binding FooError}/>
Then perhaps your save button could check for errors too.
I'd strongly advise you to look into INotifyDataErrorInfo, which is a great way to organise your errors in a way that WPF can easily display (instead of properties like FooError). It might seem like a lot of work the first time you use it, but it's great once you've got lots of controls and validation rules.

Related

WPF/MVVM: Dynamically add checkboxes to 2 ColumnGrid based on available Enums

I'm currently build a gui for a logger. The logger class has a enum Types with a couple of values defining the different log message types.
Now I want to create a gui for this logger. Part of this gui is a 2 column grid where a checkbox for each log message type is available. It shall be used to filter the log. In order to reduce future coding effort, I would like to add a checkbox for each available type of log message dynamically. So when ever a new value is added to Logger.Types the gui adapts automatically.
My xaml of the checkboxes look something like this:
<CheckBox Name="CheckBoxTypeInformation" Grid.Column="0" Grid.Row="1"
Content="{myControls:LocalizationMarkup 'LOGMSG_TYPE_INFORMATION'}"
Command="{Binding FilterTypeCommand}" CommandParameter="Information"
IsChecked="{Binding Path=FilterType[0]}"/>
So there is quite some work to be done for each checkbox:
Insert the cb into the 2 column grid in some manner by defining the Grid.Column and Grid.Row. The logic of it is not the problem
Set the Content with a custom LocalizationMarkup
Bind a command with dynamic CommandParameter. Here I would like to pass the actual enum value. But I was not able to do so in xaml atm.
Bind the IsChecked Property to an index in an array holding the IsChecked Status of all checkboxes.
I'm not quite shure how to handle this. As I understood it correctly I somehow have to add an ObservableCollection<> of a new custom class which has properties for each of the 4 wanted tasks.
I hope somebody can point me into the right direction on how to implement something dynamic as described in a propper mvvm/wpf manner.
Thank you very much!
I would't use a grid for that, but simply a List.
Define a classe named LogLevelViewModel with two properties
private string _logDetails;
public string LogLevelDetails
{
get
{
return _logDetails;
}
set
{
if (_logDetails == value)
{
_logDetails = value;
RaisePropertyChanged(nameof(LogLevelDetails));
}
}
}
private LogLevel _logLevel;
public LogLevel LogLevel
{
get
{
return _logLevel;
}
set
{
if (_logLevel == value)
{
_logLevel = value;
RaisePropertyChanged(nameof(LogLevel));
}
}
}
Then define on your view model a ObservableCollection LogLevelViewModels
On the constructor of the view model, you can populate it With:
foreach (LogLevel level in Enum.GetValues(typeof(LogLevel )))
{
LogLevelViewModels.Add(level );
}
On the XAML make sure to define a DataTemplate for LogLevelViewModel and that's it.

How to control DynamicResource implementation in C#

In my program I would like to implement a DynamicResource from code-behind. Right now I am binding the Content of a Label to a string property in my Data Model...
<Label Content="{Binding DataModel.StringValue}" ... />
Following this question, I have implemented the string in my Data Model like so:
private string _stringValue = (string)Application.Current.Resources["nameOfResource"];
public string StringValue
{
get { return _cartsInSystem; }
set
{
_cartsInSystem = value;
NotifyPropertyChange(() => CartsInSystem);
}
}
I would like to make it so that every time the user changes the Resource Dictionary, this string value updates with the new value.
I am trying to achieve the same effect as something like this:
<Label Content="{DynamicResource nameOfResource}" ... />
Please let me know what I am doing wrong, and how I might correctly implement something like this.
UPDATE 1: As requested by #HighCore, this is an example of my code where I only have access to string values from code-Behind (or C# class)
(This is part of a ViewModel of a TreeView in my MainWindow)
//The "DisplayNames" for these nodes are created here and not accessible through xaml.
//This is because the xaml window has access to this code through it's itemsSource
private HierarchicalVM CreateCartsNode()
{
return new HierarchicalVM()
{
DisplayName = "Carts",
Children =
{
new CartConnection() { ConnectionDataModel = new CartConnectionModel(), DisplayName = "Cart Connection" },
new HierarchicalVM() {
DisplayName = "Cart Types",
Children = {
CreateCartType( new CartConfigModel() { DisplayName = "Default" }, new CartIO_Model() ),
},
Commands = { new Command(OpenAddCart) {DisplayName = "Add..."} }
}
}
};
}
This is the xaml of the above TreeView:
<!-- Tree view items & Functions -->
<TreeView ItemsSource="{Binding DataTree.Data}" ... />
Update 2: I have another perfect example of my problem...
I have a comboBox that has it's itemsSource bound to an ObservableCollection in my Data Model. Like so:
private ObservableCollection<string> _objCollection;
private string _notUsed = "Not Used";
private string _stop = "Stop";
private string _slow = "Slow";
public DataModel()
{
ObjCollection = new ObservableCollection<string>() { _notUsed, _stop, _slow };
}
public ObservableCollection<string> ObjCollection {...}
xaml:
<ComboBox ItemsSource="{Binding DataModel.ObjCollection}" ... />
If I want to make it so that the items in this comboBox change when the resource dictionary is changed, it looks like I'll need to handle it in C# rather than xaml.
After OP's UPDATE 2 and having a chat with him for a different question, I understood he was trying achieve localisation for his application. He would change Resource Dictionaries (for different languages) on the fly, and he wanted his C# code re-read/load values from Application.Current.Resources.
APPROACH ONE
After you changing the Resource Dictionary, You could use something like EventAggregator/Mediator to let other parts of the application (including ViewModels) know about Resource Dictionary change, and they respond to it by re-loading/reading resources/values from Application.Current.Resources
APPROACH TWO
OP doesn't want to introduce any new dependencies like EventAggregator/Mediator. So, I suggested this second approach. I know, it is not pretty, but here it goes..
You could have a global static event instead of EventAggregator/Mediaotr to let other parts of the application know that you swapped resource dictionary, and they will re-load/read values.
Read this answer about potential problems with static events and their subscriptions.

CheckedListBox - Collection of FileInfo objects - Display Custom ToString

I have a CheckedListBox in a WinForms app (3.5 runtime), and I am adding a bunch of FileInfo objects to the Items ObjectCollection. The problem is that I don't like what is displayed in the CheckedListBox (since the FileInfo was from a Directory.GetFiles() it just shows the FileInfo.Name of the file in the listbox).
Is there any easy way to change what is displayed in the CheckedListBox without having to create a seperate custom class/object.
I am basically doing
checkedListBox.Items.Add(fileInfo)
and the result is just the file name of the file.
Changing display member works but I can't create something custom, only the existing properties in the FileInfo class.
I want to be able to display something like Name - FullName
Example (desired):
File1.txt - C:\Path\SubPath\File1.txt
Actually, it seems like it should be possible after all. The CheckedListBox has a FormattingEnabled property and a Format event inherited from ListBox which is called before each item is displayed. So something along these lines should work:
myCheckedListBox.FormattingEnabled = true;
myCheckedListBox.Format += (s, e) => { e.Value = string.Format("{0} - {1}", ((FileInfo)e.ListItem).Name, ((FileInfo)e.ListItem).FullName); };
Haven't tested it though. See also MSDN
Old answer:
I don't think you can do it without creating a wrapper. Although 10 lines of code don't seem all that bad to me:
class FileInfoView
{
public FileInfo Info { get; private set; }
public FileInfoView(FileInfo info)
{
Info = info;
}
public override string ToString()
{
// return whatever you want here
}
}
The additional advantage to having a view model is that you can decorate it further for display purposes all the way you like.
I dont know if there is work around for this except creating a custom class and include an instance of FileInfo inside it
and in this way you either create a new property and include a custom data in it or override the ToString() function
something like (this for demonstration purposes)
MyFileInfo
{
public FileInfo TheFileInfo;
public string CustomProperty
{
get
{
if(this.TheFileInfo != null)
return this.TheFileInfo.FileName + this.TheFileInfo.FullName;
return string.Empty;
}
}
}

Can't seem to get WPF DataBinding in my head

Ok, I'm no newbie at programming or C# as such, I just can't seem to get WPF's databinding straight in my head. My colleagues are raving about it (and yes, I will ask them as well) but right now I'm stumped.
Here's what I'd like to do for starters:
As an example I've got a list of things like this:
List<Thing> thingList = Source.getList();
Now normally I'd go
foreach(Thing t in thingList)
{
//add thing to combobox
}
But from what I can gather is that I can somehow not do this but use a databinding to populate the combobox for me.
What I can't seem to get is where do I put the 'thingList'? Do I make it a separate property somewhere? Where do I put that property?
I feel very stupid at the moment, as I've been struggling with this for a while now and I can't find any examples out there that make me understand this - probably very simple - concept.
Anyone out there willing to help me or point me at some step-by-step guide I might have missed?
Use ObservableCollection<T> for databinding in WPF. Where T is your class. See example below
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("A", "E"));
Add(new PersonName("B", "F"));
Add(new PersonName("C", "G"));
Add(new PersonName("D", "H"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
this.firstName = first;
this.lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
Now in XAML. Go to resource tag
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample"
x:Class="Sample.Window1"
Width="400"
Height="280"
Title="MultiBinding Sample">
<Window.Resources>
<c:NameList x:Key="NameListData"/>
</Window.Resources>
<ListBox Width="200"
ItemsSource="{Binding Source={StaticResource NameListData}}" // Name list data is declared in resource
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
xmnls:c will give you option to choose the class. Here you can choose c,d ,e x whatever but be sure it should be used earlier
When it comes to data-binding i've found that this page if read thoroughly answers most of the questions beginners have about it.
To answer the question: The alernative to adding all the items is to tell the ComboBox where to get its items from, this is done with the ItemsSource property.
You can either set this in XAML or in code, while you would need a binding expression in XAML a normal assignment should do in code:
comboBox.ItemsSource = thingList;
If you do not specify further how those objects in the list are to be displayed the ToString method will be called, unless overridden you will usually end up with the class-path of your object. There are two main ways of telling the application how to display the object:
The fist and more heavy option is Data Templates which is used for displaying complex data using controls (which in turn can have items and templates etc), the second way is using lightweight properties like DisplayMemberPath, which should be set to the name of the property which should be displayed (usually just a string).
If your list changes and the combo box should be updated on its own the source should implement INotifyCollectionChanged, in most cases you will use the standard implementation ObersableCollection<T>.
Most people would use WPF Databinding to populate the combobox but you don't have to.
You can add the items via code if you want to.
It's easy to get trapped into doing everything as it "should" be done without have a good reason for doing it that way (BTW, I'm not recommending you manually add the items, I'm just saying you can).
List<Thing> thingList = Source.getList();
foreach(Thing t in thingList)
{
combobox.Items.Add( t );
}

Click a custom control and show some variables related to it in another control

in my Win Forms app I create an array of dynamic custom controls inside a loop. These, lets call them 'boxes', are like my basic pieces of information. I also create string arrays in other parts of the code that contain the information of this 'boxes', so that for example string[3] is a variable of box[3] and so does stringa[3], stringb[3], stringc[3]... all the arrays with the same index are related to the box with that index. Hope I make myself clear.
Only 2 of this strings are shown in 2 labels inside each custom control 'box' in the array, but the others are there because I want to make something so that when the user clicks one of these controls the other strings can be shown in another control. Sort of something like "More Information...". All the 'boxes' in the array need to have the same event handler because I create +100.
To put it more into context, each custom control 'box' in the array shows the Symbol and the Price of a stock and I want that when the user clicks on each stock more quote information is shown on another special control which is like a placeholder for "More info".
I am thinking of 2 ways to do it:
If I could "detect" the index of the clicked control (which is the same in the strings related to it), I could just set this to an int j and all I have to do is show all the strings a,b,c... with index j. Unfortunately I cannot find a way to do this, maybe it is not even possible.
The other way I have thought is to create some properties for my custom control which "store" this variables, and in my app instead of assigning strings I would set properties for each control, which I could later retrieve when the control is clicked. I haven't tryed this because I don't know exactly how to do it.
What do you think? Do you know how can I achieve this or do you have a different idea that will work? Please help! Thanks in advance.
It's kind of a broad implementation question since there are countless ways you could implement something like this.
If you are creating two collections, one with the buttons and one with the information, you potentially could just assign each of the buttons 'Tag' properties to point to the corresponding info and assign a generic OnClick event handler that displays the info.. something like:
infoControl.text = ((InfoClass)((Button)Sender.Tag)).pieceOfInformation;
But again there are many ways to do this, and the choice comes down to how you store your information.
For your first method, you could have a property of your custom control that is the index.
public class Box : Control
{
// ...existing code
private int index;
public int Index
{
get
{
return index;
}
set
{
index = value;
}
}
}
OR
For your second method, you could have a property of your custom control that is the additional info string.
public class Box : Control
{
// ...existing code
private string extraInfo;
public string ExtraInfo
{
get
{
return extraInfo;
}
set
{
extraInfo = value;
}
}
}
In either case, you could then access the proper information right in your click handler for the "box".
i don't know about the first way - got to noodle around more, but in the second way you can extended your custom or built-in control: for example:
public class ExtendedLabel: Label
{
public string[] MoreInfo { get; set; }
}
and initialize it
public TestForm()
{
InitializeComponent();
ExtendedLabel label = new ExtendedLabel();
label.MoreInfo = new string[] { "test" };
this.Controls.Add(label);
label.AutoSize = true;
label.Location = new System.Drawing.Point(120, 87);
label.Name = "label1";
label.Size = new System.Drawing.Size(35, 13);
label.TabIndex = 0;
label.Text = label.MoreInfo[0];
}
And later in your event handler you can use the inside information

Categories