Multiple windows with the same functionalities - c#

So i was wondering how i would make some kind of superwindow/superclass for my windows. The windows look the following:
The first picture shows how every single one of the windows would potentially look when pressed on one of the buttons. So each of these windows would have the same basic functionality but with some different returning objects which can be seen in the following code.
public partial class FlatsteelWindow : Window {
private string str;
private IList test;
public FlatSteel _returnObject { get; set; }
public double amount { get; set; }
public FlatsteelWindow(object inputObject) {
InitializeComponent();
_returnObject = inputObject as FlatSteel;
FetchList();
}
private void FetchList() {
test = MaterialLogic.GetFlatsteelList() as List<FlatSteel>;
foreach (FlatSteel flatSteel in test) {
flatsteelListView.Items.Add(flatSteel.Name);
}
}
private void flatsteelListView_PreviewMouseLeftButtonUp(object sender, RoutedEventArgs e) {
var item = (sender as ListView).SelectedItem;
if (item != null) {
_returnObject = flatsteelListView.SelectedItems as FlatSteel;
str = item.ToString();
amountTextbox.IsEnabled = true;
FindObject(str);
}
}
private void FindObject(string s) {
foreach (FlatSteel flatSteel in test) {
if (s.Equals(flatSteel.Name)) {
_returnObject = flatSteel;
}
}
}
private void submitButton_Click(object sender, RoutedEventArgs e) {
if (!string.IsNullOrEmpty(amountTextbox.Text)) {
amount = amountTextbox.Text.customParseToDouble();
this.Close();
}
else {
MessageBox.Show("Indtast venligst en værdi.");
}
}
}
This is the code for the shown Flatsteel window.
Here is the code which creates the window:
private void flatsteelButton_Click(object sender, RoutedEventArgs e) {
FlatsteelWindow flatsteelWindow = new FlatsteelWindow(this);
flatsteelWindow.ShowDialog();
test = flatsteelWindow._returnObject;
amount = flatsteelWindow.amount;
if (test != null && amount != null) {
AddToGridView();
}
}
public FlatSteel test { get; set; }
private double amount { get; set; }
As we can see in this code the flatsteel object is needed where this method is invoked. The 6 different windows, which each are represented by the six buttons on the 2nd picture, each return an object to their type of material. So to make it clear what my question is:
How do i make one superclass/superwindow that has all the same basic funtionality, from which i then can inherit from and add the needed functionality for the different returning objects?

Related

Add tooltip to FlyoutMenu

I have a FlyoutMenu in my uwp app. It works fine, but I want to add tooltips to some items. Can I do that?
If you are not working on uwp, the related question here may be of interest:
Showing a tooltip for a MenuItem
class WindowsMenuFlyoutItem: Windows.UI.Xaml.Controls.MenuFlyoutItem
{
public ICommonMenuItem InnerItem { get; set; }
public WindowsMenuFlyoutItem (MyModelObject inner) {
this.Text = inner.GetTitle().Text;
this.Tapped += WindowsMenuFlyoutItem_Tapped;
// set tooltip?
}
private void WindowsMenuFlyoutItem_Tapped(Object sender,
Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
// handler here . . .
}
}
Try this:
private void WindowsMenuFlyoutItem_Tapped(object sender, TappedRoutedEventArgs e)
{
MenuFlyoutItem item = sender as MenuFlyoutItem;
ToolTipService.SetToolTip(item, "tooltip...");
}
Or if you want to set it immediately before the item is tapped:
public class WindowsMenuFlyoutItem : Windows.UI.Xaml.Controls.MenuFlyoutItem
{
public ICommonMenuItem InnerItem { get; set; }
public WindowsMenuFlyoutItem(MyModelObject inner)
{
this.Text = inner.GetTitle().Text;
this.Tapped += WindowsMenuFlyoutItem_Tapped;
Windows.UI.Xaml.Controls.ToolTipService.SetToolTip(this, "tooltip...");
}
}

How can I pass data between windows in my C# WPF application?

I have a login form in the MainWindow of my WPF application. If the user logs in successfully, I want to open the HomeWindow. My problem is that I need to pass the adminID variable from the MainWindow to the HomeWindow. How can I do this?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
int errors = 0;
if (txtUsername.Text == "")
{
lblUsernameStatus.Content = "This field is required.";
errors = errors + 1;
}
if (txtPassword.Text == "")
{
lblPasswordStatus.Content = "This field is required.";
errors = errors + 1;
}
if (errors == 0)
{
Administrator TryLogin = new Administrator();
if (TryLogin.VerifyUser(txtUsername.Text, txtPassword.Text))
{
HomeWindow home = new HomeWindow();
int adminID = TryLogin.userID;
home.Show();
this.Close();
}
else
{
lblLoginStatus.Content = TryLogin.status;
}
}
}
PS: I haven't written anything in the HomeWindow.xaml.cs file.
Define an initializer in HomeWindow to accept the data you wish to send:
private int AdminID;
public HomeWindow()
{
InitializeComponent();
}
public HomeWindow(int adminID) : base()
{
AdminID = adminID;
}
Then you can just:
HomeWindow home = new HomeWindow(TryLogin.userID);
home.Show();
this.Close();
Declaring static variable would be the simplest and easiest way because once login is authorized, the value doesn't change until logged off(application is exited)
I also used a way of passing value through Window constructor but static variables are much easier to utilize many fixed data of the logged-in users like customized setting data for each users. I also have a WPF app and pass 11 values and utilize easiily everywhere inside application.
Declare as static variable in MainWindow like,
public static int adminID;
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
adminID= TryLogin.userID;
}
And usage in HomeWindow is like,
MainWindow.adminID
Hope this helps..
There are different ways to achieve this.
Here is an example using singleton
public class User
{
private static readonly User _instance;
private static object syncRoot = new Object();
public string Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string SomeOtherProperty { get; set; }
private User()
{
// Initialize defaults
}
public void Reset()
{
// Clear existing values
}
public static User Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (_instance == null)
_instance = new User();
return _instance;
}
}
}
}
}
On your login form:
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
int errors = 0;
if (txtUsername.Text == "")
{
lblUsernameStatus.Content = "This field is required.";
errors = errors + 1;
}
if (txtPassword.Text == "")
{
lblPasswordStatus.Content = "This field is required.";
errors = errors + 1;
}
if (errors == 0)
{
Administrator TryLogin = new Administrator();
if (TryLogin.VerifyUser(txtUsername.Text, txtPassword.Text))
{
User.Instance.Reset(); // Make sure old data is removed
User.Instance.Username = txtUsername.Text;
User.Instance.Password = txtPassword.Text;
HomeWindow home = new HomeWindow();
int adminID = TryLogin.userID;
home.Show();
this.Close();
}
else
{
lblLoginStatus.Content = TryLogin.status;
}
}
}
On your home form:
You can retrieve the User credentials using User.Instance just make sure you reset it on logoff.

WPF/C#5.0 Publish/Subscribe to an array of static fields?

Using C# 5.0, I'm creating a publish/subscribe relationship on a static field, so that I can access it from multiple pages. In the host window, I have
public enum PLCStates
{
Good,
Bad,
Disabled
};
public static class PLCSafeStates
{
public static event EventHandler testStates1Changed;
private static PLCStates _testStates1;
public static PLCStates testStates1
{
get { return _testStates1; }
set
{
if (value != _testStates1)
{
_testStates1 = value;
if (testStates1Changed != null)
testStates1Changed(null, EventArgs.Empty);
}
}
}
}
And then in the pages hosted by the window, I have things like:
public FB1()
{
InitializeComponent();
SafteyFaults.PLCSafeStates.testStates1Changed += PLCSafeStates_testStates1Changed;
}
private void PLCSafeStates_testStates1Changed(object sender, EventArgs e)
{
var test2 = SafteyFaults.PLCSafeStates.testStates1;
if (test2 == SafteyFaults.PLCStates.Bad)
{
VisualStateManager.GoToState(btnFB, "PLCBad", true);
}
if (test2 == SafteyFaults.PLCStates.Good)
{
VisualStateManager.GoToState(btnFB, "PLCGood", false);
}
}
private void btnFB_Click(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(btnOut1, "PLCBad",false);
if (SafteyFaults.PLCSafeStates.testStates1 == SafteyFaults.PLCStates.Good)
SafteyFaults.PLCSafeStates.testStates1=SafteyFaults.PLCStates.Bad;
else
SafteyFaults.PLCSafeStates.testStates1 = SafteyFaults.PLCStates.Good;
}
(right now, I don't have any business logic wired up yet- once I get this working, I'll link to actual data).
Anyhow, all of this works to create a single field I can subscribe to, modify, etc. But I need 20+ of these fields. I want to make 'testStates1' an array, but I've not been able to get it to work.
If I make the following edits to the code shown so far, it compiles and runs, but throws an error when I actually try to access the field (e.g. click on the button to change it):
//window
public static class PLCSafeStates
{
public static event EventHandler testStates1Changed;
private static PLCStates[] _testStates1;
public static PLCStates[] testStates1
{
get { return _testStates1; }
set
{
if (value != _testStates1)
{
_testStates1 = value;
if (testStates1Changed != null)
testStates1Changed(null, EventArgs.Empty);
}
}
}
}
//page
public FB1()
{
InitializeComponent();
SafteyFaults.PLCSafeStates.testStates1Changed += PLCSafeStates_testStates1Changed;
}
private void PLCSafeStates_testStates1Changed(object sender, EventArgs e)
{
var test2 = SafteyFaults.PLCSafeStates.testStates1[0];
if (test2 == SafteyFaults.PLCStates.Bad)
{
VisualStateManager.GoToState(btnFB, "PLCBad", true);
}
if (test2 == SafteyFaults.PLCStates.Good)
{
VisualStateManager.GoToState(btnFB, "PLCGood", false);
}
}
private void btnFB_Click(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(btnOut1, "PLCBad",false);
if (SafteyFaults.PLCSafeStates.testStates1[0] == SafteyFaults.PLCStates.Good)
SafteyFaults.PLCSafeStates.testStates1[0]=SafteyFaults.PLCStates.Bad;
else
SafteyFaults.PLCSafeStates.testStates1[0] = SafteyFaults.PLCStates.Good;
}

Handle control events differently depending on the state of the system

I have trying to build a simulator of Ingenico POS terminal (iWL220).
The main screen I have a combo-box. Once user enter id and password the combo-box load 6 menus. If User click btn1 then combo-box clear the menu and add another set of menu. If user click btn1 for new loaded menu then again combo-box cleared and load another set of menu so on.
My problem is for each button click (btn1, btn2, btn3, btn4, btn5) I have to code lot of if else statement. Example;
First menu (on combo-box) has 6 sector.
1.SectorA
2.SectorB
3.SectorC
4.SectorD
5.SectorE
6.SectorF
If user choose 1.SectorA then user click btn1. Then btn1 clear the combo-box and loads another set of menu. This time menu (on combo-box) has 3 companies.
1.CompanyA
2.CompanyB
3.CompanyC
This time user choose 1.CompanyA then user click again btn1. Then btn1 clear the combo-box and loads another set of menu. This time menu (on combo-box) has 2 payment option.
1.FullPayment
2.ParitalPayment
Now this time if user click btn1 or btn2 the combo-box visible become false and in main screen there is a label and text box. The text box allows user to enter the subscriber number and press enter (green button).
I already load Ingenico terminal picture as jpeg and top of it I set my buttons.
I gave only small version of my simulation. In my app there are 114 probability that user can choose.
In my app btn1 has 92 probability to be clicked, btn2 has 53 probability to be clicked and so on. After user enters the subscriber number and click green button the my app use wfc services to format the data and send to sql server.
But before user click each combination of button some where in my app I store the btn number as 422. This 422 means, user chose SectorD + CompanyB + ParitalPayment option. So my wfc will know what it is mean 422.
My question is what is the shortest way to construct my button events for this 114 probability case?
I have 4 buttons. Btn1, Btn2, Btn3 and Btn4. Also I have some arrays as it shown below and 1 combo-box.
1.ArrayMain() = {“1.Water”,”2.Air”,”3.Soil”,”4.Fire”}
1.1. ArrayWater() = {“1.Salty”,”2.Fresh”, “3.Contaminated”}
1.1.1.ArraySalty() = {1.”AA”, 2.”BB”, 3.”CC”}
1.1.2.ArrayFresh() = {1.”DD”, 2.”EE”, 3.”FF”}
1.1.3.ArrayContaminated() = {1.”XX”, 2.”YY”, 3.”ZZ”}
1.2 ArrayAir() = {“1.Fresh”, “2.Contaminated”}
1.3 ArraySoil() = {“1.Normal”, “2.Contaminated”}
1.4 ArrayFire() = {“1.Low”,”2.Mid”,”3.High”}
When my app starts, first array values 1.(ArrayMain) fills the comboBox. This comboBox will have 4 values as, “1.Water”, ”2.Air”, ”3.Soil”, ”4.Fire” in it. If user choose “1.Water” than user clicks Btn1. Than btn1 events clears the comboBox and loads 1.1ArrayWater() values into comboBox.
Second time if user chooses “1.Salty” than user clicks again btn1 and this time btn1 events clears the comboBox and loads 1.1.1ArraySalty() values into comboBox.
Third time if user chooses “2.BB” than user clicks Btn2 and sends the information “BB” for calculation.
First you have 5 (more or less) menu item and each time you press any (number) buttons (1 to 9 lilke in pos terminal) pressed than new menu appears on the screen.
Each button at any specific time shall execute some specific action depending on the state of the system. Obviously, if you try to decide the specific action depending on the multitude of different variables, you will create a lot of branching code. Such code is very difficult to write correctly and even more difficult to debug and maintain.
So, what if we encapsulate current action for each possible state(sequence of state) in some specific class (interface):
/// <summary>
/// Represents internal terminal presenter that is used inside IGlobalTerminalPresenter.
/// </summary>
public interface ITerminalPresenter
{
void UpdateUI();
ITerminalPresenter this[Int32 index]
{
get;
}
ITerminalPresenter Do1();
ITerminalPresenter Do2();
ITerminalPresenter Parent
{
get;
set;
}
void Reset();
}
Inside the form we will use field of a similar interface that will encapsulate all changes of the presenter.
/// <summary>
/// Represents terminal presenter that UI can operate upon.
/// </summary>
public interface IGlobalTerminalPresenter
{
void UpdateUI();
void Do1();
void Do2();
Int32 SelectedIndex
{
get;
set;
}
void Reset();
}
Our event handlers will become:
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
var senderComboBox = (ComboBox)sender;
this.globalTerminalPresenter.SelectedIndex = senderComboBox.SelectedIndex;
}
private void button1_Click(object sender, EventArgs e)
{
this.globalTerminalPresenter.Do1();
}
private void button2_Click(object sender, EventArgs e)
{
this.globalTerminalPresenter.Do2();
}
To allow our concrete TerminalPresenters to interoperate with form we will force our form to implement the following interface:
/// <summary>
/// This represents your UI in technology-independent manner
/// </summary>
public interface ITerminalView
{
String Title { get; set; }
String Input { get; set; }
String Output { get; set; }
String Button1_Text { get; set; }
String Button2_Text { get; set; }
IEnumerable<String> SelectionItems { get; set; }
void Clear();
}
public partial class MainForm : Form,
ITerminalView
{
...
#region ITerminalView implementation
public string Title
{
get { return this.Text; }
set { this.Text = value; }
}
public String Button1_Text
{
get { return this.button1.Text; }
set { this.button1.Text = value; }
}
public String Button2_Text
{
get { return this.button2.Text; }
set { this.button2.Text = value; }
}
public string Input
{
get { return this.textBox_Input.Text; }
set { this.textBox_Input.Text = value; }
}
public string Output
{
get { return this.textBox_Output.Text; }
set { this.textBox_Output.Text = value; }
}
public IEnumerable<string> SelectionItems
{
get { return this.comboBox.Items.Cast<String>(); }
set
{
this.comboBox.Items.Clear();
if (value == null)
return;
foreach (var item in value)
{
this.comboBox.Items.Add(item);
}
}
}
public void Clear()
{
this.comboBox.SelectedIndex = -1;
this.Title = String.Empty;
this.Input = String.Empty;
this.Output = String.Empty;
this.SelectionItems = null;
}
#endregion
For now we will create two TerminalPresenters - one to just allow selection of next option through combobox, one that calculates sum of two numbers. Both of them use the same base class.
/// <summary>
/// Base class for all presenters
/// </summary>
public abstract class TerminalPresenterBase : ITerminalPresenter
{
protected ITerminalView view;
public TerminalPresenterBase(ITerminalView view)
{
if (view == null)
throw new ArgumentNullException("view");
this.view = view;
this.Parent = this;
}
public abstract void UpdateUI();
public abstract ITerminalPresenter this[int index]
{
get;
}
public abstract ITerminalPresenter Do1();
public abstract ITerminalPresenter Do2();
public virtual ITerminalPresenter Parent
{
get;
set;
}
public virtual void Reset()
{
this.UpdateUI();
}
}
/// <summary>
/// Presenter whose sole goal is to allow user to select some other option and press next
/// </summary>
public class SelectOptionPresenter : TerminalPresenterBase
{
private IList<KeyValuePair<String, ITerminalPresenter>> options;
private ITerminalPresenter selected;
private String title;
public SelectOptionPresenter(ITerminalView view,
String title,
IList<KeyValuePair<String, ITerminalPresenter>> options)
: base(view)
{
if (options == null)
throw new ArgumentNullException("options");
this.title = title;
this.options = options;
foreach (var item in options)
{
item.Value.Parent = this;
}
}
public override void UpdateUI()
{
this.view.Clear();
this.view.Button1_Text = "Confirm selection";
this.view.Button2_Text = "Go back";
this.view.Title = title;
this.view.SelectionItems = options
.Select(opt => opt.Key);
}
public override ITerminalPresenter this[int index]
{
get
{
this.selected = this.options[index].Value;
return this;
}
}
public override ITerminalPresenter Do1()
{
return this.ConfirmSelection();
}
public override ITerminalPresenter Do2()
{
return this.GoBack();
}
public ITerminalPresenter ConfirmSelection()
{
this.selected.UpdateUI();
return this.selected;
}
public ITerminalPresenter GoBack()
{
this.Parent.UpdateUI();
return this.Parent;
}
}
public enum APlusBState
{
EnterA,
EnterB,
Result
}
public class StepActions
{
public Action UpdateUI { get; set; }
public Func<ITerminalPresenter> Do1 { get; set; }
public Func<ITerminalPresenter> Do2 { get; set; }
}
public class APlusBPresenter : TerminalPresenterBase
{
private Int32 a, b;
private APlusBState state;
private String error = null;
private Dictionary<APlusBState, StepActions> stateActions;
private void InitializeStateActions()
{
this.stateActions = new Dictionary<APlusBState, StepActions>();
this.stateActions.Add(APlusBState.EnterA,
new StepActions()
{
UpdateUI = () =>
{
this.view.Title = this.error ?? "Enter A";
this.view.Input = this.a.ToString();
this.view.Button1_Text = "Confirm A";
this.view.Button2_Text = "Exit";
},
Do1 = () => // Confirm A
{
if (!Int32.TryParse(this.view.Input, out this.a))
{
this.error = "A is in incorrect format. Enter A again";
return this;
}
this.error = null;
this.state = APlusBState.EnterB;
return this;
},
Do2 = () => // Exit
{
this.Reset();
return this.Parent;
}
});
this.stateActions.Add(APlusBState.EnterB,
new StepActions()
{
UpdateUI = () =>
{
this.view.Title = this.error ?? "Enter B";
this.view.Input = this.b.ToString();
this.view.Button1_Text = "Confirm B";
this.view.Button2_Text = "Back to A";
},
Do1 = () => // Confirm B
{
if (!Int32.TryParse(this.view.Input, out this.b))
{
this.error = "B is in incorrect format. Enter B again";
return this;
}
this.error = null;
this.state = APlusBState.Result;
return this;
},
Do2 = () => // Back to a
{
this.state = APlusBState.EnterA;
return this;
}
});
this.stateActions.Add(APlusBState.Result,
new StepActions()
{
UpdateUI = () =>
{
this.view.Title = String.Format("The result of {0} + {1}", this.a, this.b);
this.view.Output = (this.a + this.b).ToString();
this.view.Button1_Text = "Exit";
this.view.Button2_Text = "Back";
},
Do1 = () => // Exit
{
this.Reset();
return this.Parent;
},
Do2 = () => // Back to B
{
this.state = APlusBState.EnterB;
return this;
}
});
}
public APlusBPresenter(ITerminalView view) : base(view)
{
this.InitializeStateActions();
this.Reset();
}
public override void UpdateUI()
{
this.view.Clear();
this.stateActions[this.state].UpdateUI();
}
public override ITerminalPresenter this[int index]
{
get { throw new NotImplementedException(); }
}
public override ITerminalPresenter Do1()
{
var nextPresenter = this.stateActions[this.state].Do1();
nextPresenter.UpdateUI();
return nextPresenter;
}
public override ITerminalPresenter Do2()
{
var nextPresenter = this.stateActions[this.state].Do2();
nextPresenter.UpdateUI();
return nextPresenter;
}
public override void Reset()
{
this.state = APlusBState.EnterA;
this.a = 0;
this.b = 0;
this.error = null;
}
}
/// <summary>
/// Represents terminal presenter to use inside GUI. It handles current ISpecificTerminalPresenter inside itself.
/// </summary>
public class GlobalTerminalPresenter : IGlobalTerminalPresenter
{
#region Fields
private ITerminalPresenter current;
private Int32 selectedIndex;
#endregion
#region Constructors
public GlobalTerminalPresenter(ITerminalPresenter mainPresenter)
{
if (mainPresenter == null)
throw new ArgumentNullException("mainPresenter");
this.current = mainPresenter;
this.UpdateUI();
}
#endregion
public void UpdateUI()
{
this.current.UpdateUI();
}
public void Do1()
{
this.current = this.current.Do1();
}
public void Do2()
{
this.current = this.current.Do2();
}
public Int32 SelectedIndex
{
get
{
return this.selectedIndex;
}
set
{
this.selectedIndex = value;
if (value == -1)
return;
this.current = this.current[value];
}
}
public void Reset()
{
this.current.Reset();
}
}
Then we initialize them in the constructor of our form:
public partial class MainForm : Form,
ITerminalView
{
private IGlobalTerminalPresenter globalTerminalPresenter;
public MainForm()
{
InitializeComponent();
var nextLevelPresenters = new KeyValuePair<String, ITerminalPresenter>[]
{
new KeyValuePair<String, ITerminalPresenter>(
"A plus B",
new APlusBPresenter(this)),
new KeyValuePair<String, ITerminalPresenter>(
"Just empty selector",
new SelectOptionPresenter(this,
"Selector with no selection choices",
Enumerable
.Empty<KeyValuePair<String, ITerminalPresenter>>()
.ToArray()))
};
var topPresenter = new SelectOptionPresenter(this, "Select the option and press the confirm button", nextLevelPresenters);
this.globalTerminalPresenter = new GlobalTerminalPresenter(topPresenter);
}
P.S.1: These code snippets assume that you have form named MainForm that has two buttons - button1, button2, one combobox, two textBoxes - textBox_Input, textBox_Output.
P.S.2: The pattern used is close enough to Model-View-Presenter, just without DataBindings.
P.S.3 You can create more or less generic state machine Presenters if you modify APlusBPresenter code. Or try to shape up ChainXxxx... classes and interfaces.
P.S.4: And sorry for these walls of code. That's probably too much for [SO] format, so I've put ad-hoc proof of concept at GitHub - https://github.com/Podskal/StackOverflow_29870164.git. It is ugly in many aspects, but as it is, it can at least give few ideas about how to implement your own system.
P.S.5: There are a lot of problematic places in this code, so you should very carefully consider how you will build your own system from it.

ListBox forcing update of items

I created a ListBoxItem where I have a property Name and override ToString() to give back name. That works nicely when I add new items.
But now I need to force the ListBox to update the labels when I change the name of my ship. I thought Refresh or Update would do that but that doesn't work.
I might be missing something very easy here.
public class ShipListBoxItem
{
public ListBox Parent { get; set; }
public ShipType Ship { get; set; }
public ShipListBoxItem()
{
Ship = new ShipType();
}
public ShipListBoxItem(ShipType st)
{
Ship = st;
}
public override string ToString()
{
return Ship.Name;
}
public void UpdateListBox()
{
Parent.Refresh(); //My problem is here. Update doesn't work either.
}
public static ShipListBoxItem AddToListBox(ListBox lb, ShipType ship)
{
ShipListBoxItem li = new ShipListBoxItem(ship);
li.Parent = lb;
lb.Items.Add(li);
return li;
}
}
If you use a List<T> as the DataSource for the listbox it is pretty easy to have changes to items show up. It also means there is no real reason to have a special class for adding a ShipListBoxItem to a ListBox, your basic Ship class may work:
class ShipItem
{
public enum ShipTypes { BattleShip, Carrier, Destroyer, Submarine, Frigate };
public ShipTypes Ship { get; set; }
public string Name { get; set; }
public ShipItem(string n, ShipTypes st)
{
Name = n;
Ship = st;
}
public override string ToString()
{
return String.Format("{0}: {1}", Ship.ToString(), Name);
}
}
The form related stuff:
private void Form1_Load(object sender, EventArgs e)
{
// add some ships
Ships = new List<ShipItem>();
Ships.Add(new ShipItem("USS Missouri", ShipTypes.BattleShip));
Ships.Add(new ShipItem("USS Ronald Reagan", ShipTypes.Carrier));
lb.DataSource = Ships;
}
private void button1_Click(object sender, EventArgs e)
{
// change a ship name
lb.DataSource = null; // suspend binding
this.Ships[0].Name = "USS Iowa";
lb.DataSource = Ships; // rebind
lb.Refresh();
}
As an alternative, you can also tell the Listbox to use a specific property for the display using DisplayMember:
lb.DataSource = Ships;
lb.DisplayMember = "Name";
This would use the Name property in the listbox instead of the ToString method. If your list is changing a lot, use a BindingList instead. It will allow changes to the list show up in the ListBox as you add them without toggling the DataSource.
Try this
ListBox.RefreshItems()
msdn
EDIT: You can use an extended class like this:
public class FooLisBox : System.Windows.Forms.ListBox
{
public void RefreshAllItems()
{
RefreshItems();
}
}
private void button1_Click(object sender, EventArgs e)
{
(listBox1.Items[0] as ShipListBoxItem).Ship.Name = "AAAA";
listBox1.RefreshAllItems();
}
I managed to solve my problem.
Mostly, thanks Jose M.
I ran into a problem however. RefreshItems() triggers OnSelectedIndexChanged()
so my overridden class looks like this
public class MyListBox : ListBox
{
public bool DoEvents{ get; set; } // Made it public so in the future I can block event triggering externally
public MyListBox()
{
DoEvents = true;
}
public void RefreshAllItems()
{
SuspendLayout();
DoEvents = false;
base.RefreshItems(); // this triggers OnSelectedIndexChanged as it selects the selected item again
DoEvents = true;
ResumeLayout();
}
// I only use this event but you can add all events you need to block
protected override void OnSelectedIndexChanged(EventArgs e)
{
if (DoEvents)
base.OnSelectedIndexChanged(e);
}
}

Categories

Resources