I've just written a program in win forms and c# that replicates the game "Yahtzee". Although, my code did the job, and the game worked well. I felt like the code was really sloppy. The main part that bugged me was that I had five buttons on my form, that represented each yahtzee dice. when you click on a dice it means you wanted to save that dice for the next roll. so I had five different variables that stored whether the dice is saved or not (bool save1, save2, save3, save4, save5). I ended up having to use if and else if statements to decide what save button was going to be changed. I would like to know a way to somehow associate the cooresponding variable to each button so the code is much more consice and easy to read. This is similar to the code I was using, where the function below is called when either dice is clicked.
dice_clicked(object sender, EventArgs e){
if (sender == dice1){
save1 = true;
}else if (sender == dice2){
save2 = true;
}else if (sender == dice3){
save3 = true;
}else if (sender == dice4){
save4 = true;
}else if (sender == dice5){
save5 = true;
}
}
You could use a Dictionary, with the Button as the Key,
and the Boolean as the Value; then you get/set the value using the
"sender" parameter in the click handler. – Idle_Mind 19 hours ago
#Idle_Mind This seems like a great idea, could you maybe write some code, so that I can understand it better – cj32 22 mins ago
Here's some example code to play with using the Dictionary approach:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Dictionary<Button, bool> saveBtn = new Dictionary<Button, bool>();
private void Form1_Load(object sender, EventArgs e)
{
// add initial entries and set them to false
saveBtn.Add(dice1, false);
saveBtn.Add(dice2, false);
saveBtn.Add(dice3, false);
saveBtn.Add(dice4, false);
saveBtn.Add(dice5, false);
}
private void dice_clicked(object sender, EventArgs e)
{
Button sourceBtn = (Button)sender;
saveBtn[sourceBtn] = true; // set save state to true for that button
// example code to reset all values to false:
foreach(KeyValuePair<Button, bool> kvp in saveBtn)
{
saveBtn[kvp.Key] = false;
}
// example to find all the buttons that have been saved:
foreach (KeyValuePair<Button, bool> kvp in saveBtn)
{
if (kvp.Value)
{
Button btn = kvp.Key;
// do something with "btn"
btn.BackColor = Color.Red;
}
}
// is dice3 saved?
if (saveBtn[dice3])
{
Console.WriteLine("dice3 was saved");
}
}
}
You could store the dice number in the Tag property of the buttons and use an array for the save states
bool[] save = new bool[5]; // Index range 0 to 4.
dice_clicked(object sender, EventArgs e)
{
int diceNo = Int32.Parse(((Button)sender).Tag);
save[diceNo - 1] = true;
}
If you numbered the dice and buttons starting with 0 instead of 1, the indexes and the numbers would match. Array indexes are always 0-based.
Related
I have a GridView control of xtraGrid suite in a form.
When I open the form for first time it is AllowEdit = false. I want that when I press on add new row link(built in by control) to make editable this only new inserted row. I read that I should use ShowingEditor event but I don't know how.
I wrote this so far but this does not editable the row:
private void gridViewNote_ShowingEditor(object sender, System.ComponentModel.CancelEventArgs e)
{
//this is first tryout
//if (gridViewNote.IsNewItemRow(gridViewNote.FocusedRowHandle))// == gridViewNote.GetFocusedDataRow())
//{
// gridColumnStagione.OptionsColumn.AllowEdit = true;
//}
//second tryout
GridView view = sender as GridView;
SchedeMaterialiDaTaglioDS.SMTAGL_NOTERow currentRow = gridViewNote.GetFocusedDataRow() as SchedeMaterialiDaTaglioDS.SMTAGL_NOTERow;
SchedeMaterialiDaTaglioDS.SMTAGL_NOTEDataTable changesTable = dsSchMatTaglio.SMTAGL_NOTE.GetChanges() as SchedeMaterialiDaTaglioDS.SMTAGL_NOTEDataTable;
e.Cancel = !view.IsNewItemRow(view.FocusedRowHandle) &&
!changesTable.Contains(currentRow);// set.Inserts.Contains(order);
}
I hope I understood your question. A few simple ways of doing this:
Adding a repository item to each column and handle the ShowingEditor event, using e.Cancel if this is supposed to be read only.
Popping up a window/textboxes, letting the user insert values and add the row with values already inserted via code.
assigning two different repository items to the same column using gridView.CustomRowCellEdit event. like such:
RepositoryItemTextEdit rep = new RepositoryItemTextEdit();
RepositoryItemTextEdit noRep = new RepositoryItemTextEdit();
noRep.ReadOnly = true;
private void button1_Click(object sender, EventArgs e)
{
gridView1.AddNewRow();
justAddedName = true;
gridView1.RefreshData();
}
private void gridView1_CustomRowCellEdit(object sender, DevExpress.XtraGrid.Views.Grid.CustomRowCellEditEventArgs e)
{
if (e.Column == colname)
{
if (e.RowHandle == gridView1.RowCount - 1 && justAddedName)
{
e.RepositoryItem = rep;
}
else
{
e.RepositoryItem = noRep;
}
}
}
It's not complete, just a direction to explore.
Hope I helped.
I have a ToolStrip with multiple ToolStripDropDownButtons, each has a set of DropDownItems.
When the user clicks on an DropDownItem, the check mark is shown.
By default, multiple items can be clicked and therefore multiple check marks appear.
What I'm trying to do is when the user clicks one DropDownItem, the other already checked items should be unchecked. In other words, there should always be only one checked item in the DropDown list.
I've been dabbling with it for some time but I can't really figure out how to keep the current checked item as it is while uncheck other items.
Below is the code I have as of now.
private void subietm1ToolStripMenuItem_Click(object sender, EventArgs e)
{
UncheckOtherToolStripMenuItems(sender);
}
public void UncheckOtherToolStripMenuItems(object selectedMenuItem)
{
List<ToolStripDropDownButton> dropdownButtons = new List<ToolStripDropDownButton>();
foreach (ToolStripItem item in toolStrip1.Items)
{
if (item is ToolStripDropDownButton)
{
dropdownButtons.Add((ToolStripDropDownButton)item);
}
}
foreach (ToolStripDropDownButton btn in dropdownButtons)
{
foreach (ToolStripMenuItem d in btn.DropDownItems)
{
if (d.Checked)
d.CheckState = CheckState.Unchecked;
}
}
}
If someone could shed some light on this or tell me an easy way to go about it, I'd be grateful.
Thank you.
So easy...
Implement their method as described below:
private void subietm1ToolStripMenuItem_Click(object sender, EventArgs e)
{
UncheckOtherToolStripMenuItems((ToolStripMenuItem)sender);
}
public void UncheckOtherToolStripMenuItems(ToolStripMenuItem selectedMenuItem)
{
selectedMenuItem.Checked = true;
// Select the other MenuItens from the ParentMenu(OwnerItens) and unchecked this,
// The current Linq Expression verify if the item is a real ToolStripMenuItem
// and if the item is a another ToolStripMenuItem to uncheck this.
foreach (var ltoolStripMenuItem in (from object
item in selectedMenuItem.Owner.Items
let ltoolStripMenuItem = item as ToolStripMenuItem
where ltoolStripMenuItem != null
where !item.Equals(selectedMenuItem)
select ltoolStripMenuItem))
(ltoolStripMenuItem).Checked = false;
// This line is optional, for show the mainMenu after click
selectedMenuItem.Owner.Show();
}
One detail is that you can implement the same method for all click menuItens, for this add same call for method UncheckOtherToolStripMenuItems((ToolStripMenuItem)sender); into the Event click for each ToolstripMenuItem, see this example to the another two ToolstripMenuItens:
private void subietm2ToolStripMenuItem_Click(object sender, EventArgs e)
{
UncheckOtherToolStripMenuItems((ToolStripMenuItem)sender);
}
private void subietm3ToolStripMenuItem_Click(object sender, EventArgs e)
{
UncheckOtherToolStripMenuItems((ToolStripMenuItem)sender);
}
I just set all the items in my menu with the event of item_Click so if one is clicked then it will just run the code below. Dont need an event for each button that way.
private void item_Click(object sender, EventArgs e)
{
// Set the current clicked item to item
ToolStripMenuItem item = sender as ToolStripMenuItem;
// Loop through all items in the subMenu and uncheck them but do check the clicked item
foreach (ToolStripMenuItem tempItemp in (ToolStripMenuItem)item.OwnerItem.DropDownItems)
{
if (tempItemp == item)
tempItemp.Checked = true;
else
tempItemp.Checked = false;
}
}
If you want to add several items to your list during runtime and have them connected in the way above you can run the below code.
private void subItemsMenus(ToolStripMenuItem parentItem, string[] listItems)
{
// Clear tool strip items first
parentItem.DropDownItems.Clear();
// Add items that are in the list
foreach (string subMenuItem in listItems)
{
ToolStripMenuItem item = new ToolStripMenuItem();
//Name that will appear on the menu
item.Text = subMenuItem;
//Put in the Name property whatever necessary to retrieve your data on click event
item.Name = subMenuItem;
//On-Click event
item.Click += new EventHandler(item_Click);
//Add the submenu to the parent menu
parentItem.DropDownItems.Add(item);
}
I have another way which works:
Each item is going to ToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
and every item has its own tag 1, 2, 3, 4, 5.
One item is checked on start and has tag = 1.
int selecteditem = 1;
bool atwork = false;
private void dzienToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
{
if (atwork) return;
else atwork = true;
selecteditem = Convert.ToInt32(((ToolStripMenuItem)sender).Tag);
foreach (ToolStripMenuItem it in sometooltipdropdown.DropDownItems)
{
if (Convert.ToInt32(it.Tag) != selecteditem)
{
it.Checked = false;
}
}
atwork = false;
}
The easiest way is add DropDownItemClicked Event and create own method:
private void toolStripDropDownButton1_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem != null)
{
CheckSelected((ToolStripDropDownButton)sender, e.ClickedItem);
}
}
private void CheckSelected(ToolStripDropDownButton button, ToolStripItem selectedItem)
{
foreach (ToolStripMenuItem item in button.DropDownItems)
{
item.Checked = (item.Name == selectedItem.Name) ? true : false;
}
}
You could get the count by copying to an array and then use extensions.
ToolStripItem[] controls = new ToolStripItem[ToolStrip.DropDownItems.Count];
ToolStrip.DropDownItems.CopyTo(controls,0);
intcheckCount = controls.Count(c => (c as ToolStripMenuItem).Checked);
if (checkCount == 0) // must keep 1 selection
item.Checked = true;
else if (checkCount > 1) //uncheck all others
controls.Cast<ToolStripMenuItem>().Where(c => c.Checked && c.Name != item.Name)
.ToList().ForEach(s => s.Checked = false);
When a user clicks a button, it starts some task. I don't want to block the main application thread, so I run it in a separate thread. Now I need to forbid a user to click the button until my task finishes.
I could set
button.Enabled = false;
, but I'm looking for some way to ignore clicks on it.
I could add some check in click event handler:
if (executingThread != null) return;
, but I will have to do it for each handler which is bad idea.
I know that there is some way to filter user's messages. Could you point me how to do this? And I don't want to filter out all messages, because some other buttons must stay clickable, I need to filter out messages that come to particular controls (buttons,grids and etc).
SOLUTION
internal class MessagesFilter: IMessageFilter
{
private readonly IntPtr ControlHandler;
private const int WM_KEYUP = 0x0101;
public MessagesFilter(IntPtr ControlHandler)
{
this.ControlHandler = ControlHandler;
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
// TODO: Add MessagesFilter.PreFilterMessage implementation
if (m.Msg == WM_KEYUP)
{
if (m.HWnd == ControlHandler)
{
Keys k = ((Keys) ((int) m.WParam));
if (k == Keys.Enter)
return true;
}
}
return false;
}
#endregion
}
As always, the UI should be presented in such a way that user understands what the application is doing and should talk to the user with UI elements.
As Adam Houldsworth suggested I would also prefer keeping the button either disabled or enabled but I would also suggest that the caption of the button should convey the message to the user that the long processing is in progress when the new thread starts..and so the caption of the button should be immediately changed to something like "Processing..Please wait..." (in addition to being disabled or even if you want to keep it enabled), and then if you have kept the button enabled just check the caption of the button (or a isProcessing bool flag) on its click event to return if it says "Processing..Please wait..." or (isProcessing == true).
Lots of the Websites which help users to upload files/images change the Upload button's caption to "Uploading..Please wait..." to inform the user to wait until the upload finishes and additionally some sites also disable the upload button so that the user is not able to click again on Upload button.
You would need to also revert back the caption to normal when the thread finishes long processing.
There may be other advanced ways but the idea is to keep it as simple and basic as possible.
Look at this example on Threading in Windows Forms which shows to disable the button while multi-threading.
+1 for all the suggestions so far. As CSharpVJ suggests - My idea was to additionally inform the user by changing the button's caption making the UI design more intuitive
This can be achieved elegantly with Backgroundworker component in Winforms [No hassles code]. Just copy-paste and HIT F5 (After creating a New Winforms Project with a Button and a Label on it)!
You do not have to check anything related to button here. Everything will be taken care by the appropriate event handlers. its just that you have to do correct stuffs int he resepctive event handlers. Try it !
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
}
/// do time consuming work here...
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
for (i = 0; i <= 199990000; i++)
{
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// Report UI abt the progress
_worker.ReportProgress(percentComplete);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Enabled = true;
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
label1.Text = "Cancelled the operation";
}
else if (e.Error != null)
{
// Do something with the error
}
button1.Text = "Start again";
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
private void OnStartClick(object sender, System.EventArgs e)
{
_worker.RunWorkerAsync();
button1.Text = "Processing started...";
button1.Enabled = false;
}
}
}
As mentioned in other answers, there is probably a better solution than what you are asking for.
To directly answer your question, check out the IMessageFilter interface
Create your filter to have it suppress the mouse messages you don't desire, apply it when necessary using Application.AddMessageFilter().
Something along these lines (this should probably compile...):
public class MouseButtonFilter : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_LBUTTONDBLCLK = 0x0203;
private const int WM_RBUTTONDOWN = 0x0204;
private const int WM_RBUTTONUP = 0x0205;
private const int WM_RBUTTONDBLCLK = 0x0206;
private const int WM_MBUTTONDOWN = 0x0207;
private const int WM_MBUTTONUP = 0x0208;
bool IMessageFilter.PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_LBUTTONDOWN:
/* case ... (list them all here; i'm being lazy) */
case WM_MBUTTONUP:
return true;
}
return false;
}
}
Greetings,
I want to write code that executes within an event handler, inside a WPF Windows application, that can detect a keypress, specifically an "Escape" character keypress, within a processing loop. This will allow the user to escape from processing. I realize this may be accomplished with some kind of multi-threaded approach, but the problem seems so simple I wondered if it might be accomplished as follows:
// Attempt 1: See if Keyboard static IsKeyDown method detects key presses while executing.
// Note that this was not successful. The Keyboard states do not appear to be updated during processing.
bool iskeypressed = false;
while (!iskeypressed)
{
System.Threading.Thread.Sleep(1000);
if (Keyboard.IsKeyDown(Key.Enter))
iskeypressed = true;
}
So, on to attempt #2. I saw some articles and samples using the Pinvoke "GetKeyboardState" method. I'm not sure I used the method correctly, but here is my attempt. It is a bit clumsy to refer to a Windows.Forms enumeration in a WPF application, but it seems like it could work.
// Attempt 2: Use Pinvoke GetKeyboardState method.
// So far, I've been unsuccessful with this as well, but I'm not sure my usage is correct.
bool iskeypressed = false;
while (!iskeypressed)
{
System.Threading.Thread.Sleep(1000);
if (isEscapePressed())
iskeypressed = true;
}
}
[DllImport("user32.dll")] public static extern int GetKeyboardState(byte[] lpKeyState);
private bool isEscapePressed()
{
byte[] keyboardState = new byte[255];
int keystate = GetKeyboardState(keyboardState);
if (keyboardState[(int)System.Windows.Forms.Keys.Escape] == 128)
return true;
else
return false;
}
But unfortunately, I'm not seeing any change in the keyboard states as this executes. I also played around a little with calls to the Dispatcher to see if I could get the keyboard information to refresh during processing, but I have not been successful with any technique.
I'm out of ideas. Can someone propose something? Thank you in advance for your assistance.
David
Something like this:
private bool IsCancelled { get; set; }
private void OnButtonClick(object sender, EventArgs e)
{
Action doWorkDelegate = DoWork;
doWorkDelegate.BeginInvoke(null, null);
}
protected override void OnKeyDown(KeyEventArgs e) {
if (e.Key == Key.Escape) {
IsCancelled = true;
e.Handled = true;
} else {
base.OnKeyDown(e);
}
}
private void DoWork()
{
IsCancelled = false;
while (!IsCancelled)
{
System.Threading.Thread.Sleep(1000);
}
}
The important point is that the method that does the work is executed in a separate thread so the main thread can process user input (key strokes).
You can not detect a key event while you are blocking WPF by executing a very long loop. You must use a multithreaded approach or you have to split the loop.
Using a BackgroundWorker is an easy way to let WPF continue handling the frontend while executing the loop.
private BackgroundWorker bw;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (bw != null)
return;
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += (senderBw, eBw) =>
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
bw.ReportProgress(i);
if (eBw.Cancel)
return;
}
};
bw.ProgressChanged += (senderBw, eBw) =>
{
//TODO set progressbar to eBw.ProgressPercentage
};
bw.RunWorkerCompleted += (senderBw, eBw) =>
{
this.bw = null;
//TODO frontend stuff (hide progressbar etc)
};
bw.RunWorkerAsync();
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (this.bw != null && this.bw.IsBusy && e.Key == Key.Escape)
this.bw.CancelAsync();
}
Trying to implement drag and drop between 2 listboxes and all examples I've seen so far don't really smell good.
Can someone point me to or show me a good implementation?
Here's a sample form. Get started with a new WF project and drop two list boxes on the form. Make the code look like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
listBox1.Items.AddRange(new object[] { "one", "two", "three" });
listBox1.MouseDown += new MouseEventHandler(listBox1_MouseDown);
listBox1.MouseMove += new MouseEventHandler(listBox1_MouseMove);
listBox2.AllowDrop = true;
listBox2.DragEnter += new DragEventHandler(listBox2_DragEnter);
listBox2.DragDrop += new DragEventHandler(listBox2_DragDrop);
}
private Point mDownPos;
void listBox1_MouseDown(object sender, MouseEventArgs e) {
mDownPos = e.Location;
}
void listBox1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button != MouseButtons.Left) return;
int index = listBox1.IndexFromPoint(e.Location);
if (index < 0) return;
if (Math.Abs(e.X - mDownPos.X) >= SystemInformation.DragSize.Width ||
Math.Abs(e.Y - mDownPos.Y) >= SystemInformation.DragSize.Height)
DoDragDrop(new DragObject(listBox1, listBox1.Items[index]), DragDropEffects.Move);
}
void listBox2_DragEnter(object sender, DragEventArgs e) {
DragObject obj = e.Data.GetData(typeof(DragObject)) as DragObject;
if (obj != null && obj.source != listBox2) e.Effect = e.AllowedEffect;
}
void listBox2_DragDrop(object sender, DragEventArgs e) {
DragObject obj = e.Data.GetData(typeof(DragObject)) as DragObject;
listBox2.Items.Add(obj.item);
obj.source.Items.Remove(obj.item);
}
private class DragObject {
public ListBox source;
public object item;
public DragObject(ListBox box, object data) { source = box; item = data; }
}
}
the proper way to do a drag-drop control in .net is by running code in the 2nd control's DragDrop event handler.
It may "smell" weird, but this is how it works in .NET.
Google gave this: http://www.codeproject.com/KB/dotnet/csdragndrop01.aspx
It seems a pretty reasonable tutorial. If it smells bad, I think that's more to do with the API for drag and drop being awkward to use rather than the tutorial itself being poor.