Create/Increment ObservableCollection On Button Click - c#

I am looking to see if it is possible to be able to increment an ObservableCollectionevery time a button is clicked?
ObservableCollection<string> _title = new ObservableCollection<string>();
public ObservableCollection<string> Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged(() => Title);
}
}
As shown from the C# code above, I have an ObservableCollection for a Title. Currently, when I add a new title, every title goes into the same collection. However, what I am aiming to achieve is; every time the "Add Title" button is pressed, a new title is added and a new ObservableCollection is created. Is this possible, and how can it be done?
EDIT1
At the moment I dynamically create Textboxes and then add whatever string I want to that Textbox. From there I save the Stackpanel, named 'Content', into a .txt file. In this file it will hold the Textboxs that have been created. (It does not save the string into that file due to the textbox being binded). I then thought the strings would be saved into a list, and when I load the Stackpanel back up from the .txt file the string would get added back to the Textbox.
EDIT2
I have changed a bit of code:
public ViewModel()
{
this.AddTitleCommand = new RelayCommand(new Action<object>((o) => OnAddTitle()));
}
private void OnAddTitle()
{
NewTitle += titleName;
}
When doing this it is not adding my string as a word it is separating the letters in my string in separate titles.

If I understand you correctly, each time your Button is pressed, you want to add a new string into a new collection. You then said that you will add other values into each collection... this sounds like you're trying to fulfil your requirements in the wrong way, but you didn't tell us what those were, so we can't help you with that. Here's how you can add a new collection each time:
private string newTitle = string.Empty;
private ObservableCollection<ObservableCollection<string>> collections = new
ObservableCollection<ObservableCollection<string>>();
public ObservableCollection<ObservableCollection<string>> Collections
{
get { return collections; }
set { collections = value; OnPropertyChanged(() => Collections); }
}
public string NewTitle
{
get { return newTitle; }
set { newTitle = value; OnPropertyChanged(() => NewTitle); }
}
public void AddCollection()
{
ObservableCollection<string> collection = new ObservableCollection<string>();
collection.Add(NewTitle);
Collections.Add(collection);
}
The NewTitle property could be data bound to a TextBox in the UI allowing users to enter the new values and when the Button is pressed, the AddCollection method would add it into a new collection and then add that into the Collections collection.
I still think that this is not a good idea though.
UPDATE >>>
Please stop what you're doing... programs are not written like that. We save data, the strings, not UI elements. There is absolutely no point in saving the UI elements along with all their extra property values that you have no interest in. Whatever method you have of displaying the strings in the TextBoxes can be reused each time the data is loaded.

ObservableCollection provides a constructor that takes an IEnumerable so use it to create a new instance with the same titles and add your new title afterward :
ObservableCollection<string> newCollection = new ObservableCollection<string>(Title);
newCollection.Add(theNewTitle)
Title = newCollection;

Related

How to avoid the overwriting in the DataBinding?

im new at programming and need help here... I want to create a Binding with a Combobox Item.
But the DataBinding is not adding a new DataBind, it overwrites the old one because of the loop. So i want if you select a "Profilname" in the Combobox that the "Path" will be displayed.
But so far, just the last loaded .txt file will be displayed because of the overwrite.
Here is now my question: How to avoid the overwrite of the DataBind in the (foreach)-loop?
For information: There is a folder which contains many .txt-files, which are all called: "profile.txt". The Programm search for all the files with a loop and then search in the files with another loop a line, which contains the word "profile_name". And then the Name has to be displayed in the ComboBox and the Path has to be binded to the "Item"/"Text" in the ComboBox.
I hope this is understandable and sorry if my code is confusing or not very strong written, im learning...
foreach (string profiletxt in Directory.EnumerateFiles(profiledirectory, "profile.txt", SearchOption.AllDirectories))
{
foreach (string line in System.IO.File.ReadAllLines(profiletxt))
{
if (line.Contains("profile_name"))
{
string remLine = line.Remove(0, 15);
string dLine = remLine.Replace("\"", "");
// dataBinding
var listProfiles = new List<Profile>() {
new Profile() {Path = profiletxt, Name = dLine,},
};
materialComboBox1.DataSource = listProfiles;
materialComboBox1.DisplayMember = "Name";
materialComboBox1.ValueMember = "Path";
}
}
if (materialComboBox1.SelectedIndex == -1)
{
MessageBox.Show("Error, couldn't find Profiles");
}
}
public class Profile
{
public string Path { get; set; }
public string Name { get; set; }
}
a ComboBox uses its ItemSource containing the available items. In your inner foreach loop you declare a new profile list for every find of profile item:
var listProfiles = new List<Profile>() {
new Profile() {Path = profiletxt, Name = dLine,},
};
materialComboBox1.DataSource = listProfiles;
Instead, you'd probably like to create a new Profile list before the first foreach loop
var listProfiles = new List<Profile>();
and in the inner loop, add your new finding to the list
listProfiles.Add(new Profile() {Path = profiletxt, Name = dLine});
Then, after the outer loop, you may assign the new ItemSource only once.
There are other newby design flaws in your code:
there should be no need to set DisplayMember and ValueMember in the .xaml.cs "code behind". Rather it belongs into the xaml code itself as these are static.
As a more general advise, consider not doing any kind of "business rules stuff" or data holding in your code behind. Rather you like to separate your UI ("View") from your data ("Model") while a "ViewModel" separates these two and implements the business rules. There are tons of good introductions on this MVVM programming pattern out there.

In WPF, how do I change the source of an image programatically? [duplicate]

This question already has answers here:
How to load image to WPF in runtime?
(2 answers)
Closed 1 year ago.
I have a custom control with a default image that I want to change based on which iteration of the control it is. For example, I have one for "F1" and "NumLock" and so on. In the constructor of the control, I have this:
public FixerBox(Dictionary<string,string> deets)
{
InitializeComponent();
btnOff();
this.FixerTitle.Text = deets["Title"];
this.FixerDesc.Text = deets["Description"];
this.FixerTags.Text = deets["Tags"];
this.FixerImg.Source = new BitmapImage(new Uri(deets["Img"], UriKind.Relative));
}
The bitmap stuff was based on another answer and produces this:
Below is the control itself showing that it's correctly getting the title, tags, and description, but the image is bunk (on the left side, that thin grey line is the border that should be around the image).c#
If I was using HTML/CSS, I could right-click the image to see what exactly its properties are, but I don't know how to get that kind of information using WPF. The best I could manage was in the top area is a status window where I've manually printed a "Tostring" output of the first controls image source data. Near as I can tell, it's all correct, but there's no actual image there. Every subsequent control has the same output (one thin line where the image should be).
EDIT Per comments, here is some more of the information. The main XAML file loads up the controls like so in its constructor:
public partial class MainWindow : Window
{
private Fixers fixers = new Fixers();
// This is the custom control consisting mostly of various boxes
private Dictionary<string,FixerBox> fixerBoxes = new Dictionary<string, FixerBox> { };
public MainWindow()
{
InitializeComponent();
var fixNames = fixers.FixerNames();
foreach (string key in fixNames)
{
fixerBoxes[key] = new FixerBox(fixers.GetFix(key));
FixersArea.Children.Add(fixerBoxes[key]);
}
StatusBox.Text += fixerBoxes["F1"].FixerImg.Source.ToString();
}
}
The fixers variable is of class Fixers which consists of the below (abbreviated to show just the F1 function for brevity):
class Fixers
{
private string ClearWS(string str)
{
var first = str.Replace(System.Environment.NewLine, "");
return first.Replace("\t", "");
}
// Loads registry functions
private Regis regStuff = new Regis();
// Loads preferences from the file
private Prefs prefs = new Prefs();
// A timer to make sure the system behaves
private Timer watcher;
// Watcher action toggles
private bool watchNumL = false;
// Translation array from fix shortname to various data about them
private Dictionary<string, Dictionary<string, string>> fixers = new Dictionary<string, Dictionary<string, string>>
{
["F1"] = new Dictionary<string,string> {
["PrefName"] = "KillF1UnhelpfulHelp",
["Img"] = #"/graphics/F1key.png",
["Title"] = #"Diable F1 ""Help"" function",
["Description"] = #"
Have you ever hit the F1 key by accident and had a distracting and unhelpful window or webpage open as a result?
Windows set the F1 key to a generic help function that basically never helps and always gets in the way.
Enable this control to disable that obnoxious design choice. Note that some programs still respond to F1 on their own accord,
but this will stop the default Windows behavior in things like Windows Explorer at least.
",
["Tags"] = "#Keyboard,#Rage"
},
};
public Fixers()
{
// The readability hack above with multi-line strings introduces a bunch of extra whitespace. Let's clear that out
foreach (var fixKey in fixers.Keys)
{
fixers[fixKey]["Description"] = ClearWS(fixers[fixKey]["Description"]);
}
}
public List<string> FixerNames()
{
return fixers.Keys.ToList();
}
public bool IsFixed(string which)
{
// If we're watching, it's fixed
if ("NumL" == which) return watchNumL;
// For anything registry related
return regStuff.IsFixed(which);
}
public Dictionary<string,string> GetFix(string which)
{
return fixers[which];
}
}
if you use binding, you can create in your ViewModel a string, in which is stored the path of your image, then you can easily change programatically its path.
Then in XAML just bind image's source to the string.
In my case I have a list of objects, with the property `ImageName' :
<Image Source="{Binding DataContext.SelectedMacro.ImageName,
RelativeSource={RelativeSource AncestorType=Window}}"/>

Saving a list between applications in the .NET property settings

I have a simple list, like this:
fruitList = new FruitList();
Apple fruit1 = new Apple("red", "small");
Banana fruit2 = new Banana("yellow", "big");
fruitList.AddFruit(fruit1);
fruitList.AddFruit(fruit2);
My program displays this in a Textbox:
textbox.Text = fruitList.DescribeCurrentFruit()
+
public string DescribeCurrentFruit()
{
string description;
if (fruitStock.Count > 0)
{
description = fruitStock[fruitCurrentlyDisplayed].Description();
}
else
{
description = "No Fruits in stock";
}
return description;
}
Currently, the List's two current objects/items (fruit1, fruit2) are automatically loaded as they are a part of my Window Form's load_event. However, if they're not a part of the load_event, or if I want to add more items/objects to the list at runtime, then permanently save them, how can I do so?
Well, I can do so by saving items in the project's property settings. (Serialization is an alternative option, but far too complex for me, and I want the simplest solution.) How do I go about this? Is there any sample code? I understand I first need to add items into my properties, but struggle even at this step.

Change Textbox text from another frame(container)

I see here lot of similar question, but I still not find answer that help me in situation.
I have two frame(lets say FrameChild), one is "in" another(practically FrameChild is in this frame, lets say FrameMain).
When I insert all parameters in FrameChild and tap on button witch is on bottom of FrameMain I call method that return string...
Now when i get string i need to change textbox text in FrameChild
I have tray many way.
First idea was something like:
FrameChild frm = new FrameChild;
frm.textbox.text = "somestring";
But nothing happen.
Than i thing use some property.
in FrameChield:
public string setTicNo
{
set
{
textBox.Text = value;
}
}
in FrameMain:
FrameChild frm = new FrameChild;
frm.setTicNo = "somestring";
When i debbuging I get value, but textbox still is empty...
On the end I try to bind textbox text on setTicNo;
public string setTicNo
{
get
{
return setTicNo;
}
set
{
setTicNo = value;
}
}
Xaml:
Text = {Binding setTicNo, Mode=TwoWay,UpdateSourceTrigger=Explicit}
(here i try use more bindings, but every time i get infinite loop.
Please help , I not have more ideas..
Thanx
Did you try building a single view model and bind it to both frames, if it was passed by ref which is the default it will change the value once you do.
A side note implement a INOTIFYPROPERTYCGANGED in the View model

Testing Custom Control derived from ComboBox

I've created a control derived from ComboBox, and wish to unit test its behaviour.
However, it appears to be behaving differently in my unit test to how it behaves in the real application.
In the real application, the Combobox.DataSource property and the .Items sync up - in other words when I change the Combobox.DataSource the .Items list immediately and automatically updates to show an item for each element of the DataSource.
In my test, I construct a ComboBox, assign a datasource to it, but the .Items list doesn't get updated at all, remaining at 0 items. Thus, when I try to update the .SelectedIndex to 0 in the test to select the first item, I recieve an ArgumentOutOfRangeException.
Is this because I don't have an Application.Run in my unit test starting an event loop, or is this a bit of a red herring?
EDIT: More detail on the first test:
[SetUp]
public void SetUp()
{
mECB = new EnhancedComboBox();
mECB.FormattingEnabled = true;
mECB.Location = new System.Drawing.Point( 45, 4 );
mECB.Name = "cboFind";
mECB.Size = new System.Drawing.Size( 121, 21 );
mECB.TabIndex = 3;
mECB.AddObserver( this );
mTestItems = new List<TestItem>();
mTestItems.Add( new TestItem() { Value = "Billy" } );
mTestItems.Add( new TestItem() { Value = "Bob" } );
mTestItems.Add( new TestItem() { Value = "Blues" } );
mECB.DataSource = mTestItems;
mECB.Reset();
mObservedValue = null;
}
[Test]
public void Test01_UpdateObserver()
{
mECB.SelectedIndex = 0;
Assert.AreEqual( "Billy", mObservedValue.Value );
}
The test fails on the first line, when trying to set the SelectedIndex to 0. On debugging, this appears to be because when the .DataSource is changed, the .Items collection is not updated to reflect this. However, on debugging the real application, the .Items collection is always updated when the .DataSource changes.
Surely I don't have to actually render the ComboBox in the test, I don't even have any drawing surfaces set up to render on to! Maybe the only answer I need is "How do I make the ComboBox update in the same way as when it is drawn, in a unit test scenario where I don't actually need to draw the box?"
Since you're simply calling the constructor, a lot of functionality of the combobox will not work. For example, the items will be filled when the ComboBox is drawn on screen, on a form. This does not happen when constructing it in a unit test.
Why do you want to write a unit test on that combobox?
Can't you seperate the logic which now is in the custom control? For example put this in a controller, and test that?
Why don't you test on the DataSource property instead of the Items collection?
I'm sure that Application.Run absence cannot affects any control's behavior
I'm having the same problem with a combo box where the items are data bound. My current solution is to create a Form in the test, add the combo box to the Controls collection, and then show the form in my test. Kind of ugly. All my combo box really does is list a bunch of TimeSpan objects, sorted, and with custom formatting of the TimeSpan values. It also has special behavior on keypress events. I tried extracting all the data and logic to a separate class but couldn't figure it out. There probably is a better solution but what I'm doing seems satisfactory.
To make testing easier, I created these classes in my test code:
class TestCombo : DurationComboBox {
public void SimulateKeyUp(Keys keys) { base.OnKeyUp(new KeyEventArgs(keys)); }
public DataView DataView { get { return DataSource as DataView; } }
public IEnumerable<DataRowView> Rows() { return (DataView as IEnumerable).Cast<DataRowView>(); }
public IEnumerable<int> Minutes() { return Rows().Select(row => (int)row["Minutes"]); }
}
class Target {
public TestCombo Combo { get; private set; }
public Form Form { get; private set; }
public Target() {
Combo = new TestCombo();
Form = new Form();
Form.Controls.Add(Combo);
Form.Show();
}
}
Here is a sample test:
[TestMethod()]
public void ConstructorCreatesEmptyList() {
Target t = new Target();
Assert.AreEqual<int>(0, t.Combo.DataView.Count);
Assert.AreEqual<int>(-1, t.Combo.SelectedMinutes);
Assert.IsNull(t.Combo.SelectedItem);
}
This solve some problems if target is ComboBox or any other control:
target.CreateControl();
but I was unable to set SelectedValue it has null value, my test working with two data sources for combo box, one as data source and second is binded to selevted value. With other controls everithing working fine. In the begining I was also creating form in tests, but there is problem when form on created on our build server while tests are executed.
I did a little hack to allow this in my custom derived combobox:
public class EnhancedComboBox : ComboBox
{
[... the implementation]
public void DoRefreshItems()
{
SetItemsCore(DataSource as IList);
}
}
The SetItemsCore function instructs the base combobox to load internal items with the provided list, it's what uses internally after the datasource changes.
This function never gets called when the control is not on a form, because there are lots of checks for CurrencyManagers and BindingContexts that are failing because this components, I believe, are provided by the parent form somehow.
Anyway, in the test, you have to call mECB.DoRefreshItems() just after the mECB.DataSource = mTestItems and everything should be fine if you only depend on the SelectedIndex and the Items property. Any other behavior like databinding is probably still not functional.

Categories