WPF Added button click not firing - wpf

I have a grid of custom buttons with 1 column. When i click on one it creates a new row, with its own custom buttons (depending on which was clicked). When i click on those new button it's supposed to continue but the click event doesn't even fire. It only works on the 1rst column
First set of button :
public ModificationPage(List<Tools.Modele> lm)
{
InitializeComponent();
int index = 0;
lm.ForEach(model =>
{
Console.WriteLine(model.Libelle);
ModifButton b = new ModifButton(model,0,index);
Grid.SetColumn(b,0);
Grid.SetRow(b,index);
b.Click += ButtonClicked;
grid.Children.Add(b);
index++;
});
}
private void ButtonClicked(object sender, RoutedEventArgs e)
{
e.Handled = true; //Without this the event is fired multiples times and add more than 1 row. Removing it doesn't fix the problem
ModifButton b = sender as ModifButton;
Console.WriteLine(b.model);
AddFollowing(b)
}
private async void AddFollowing(ModifButton b)
{
List<List<String>> rep = await SQL.Procedure(Constant.RecupModel, sp);
grid.ColumnDefinitions.Add(new ColumnDefinition());
int index = 0;
rep.ForEach(item =>
{
Modele m = new Modele(int.Parse(item[0]), item[1], item[4]);
ModifButton mb = new ModifButton(m, grid.ColumnDefinitions.Count - 1,index);
b.Click += ButtonClicked; //This one doesn't work
Grid.SetColumn(mb, grid.ColumnDefinitions.Count - 1);
Grid.SetRow(mb, index);
grid.Children.Add(mb);
index++;
});

Related

Click event for button in winform is firing repeatedly for number of values in List

I have a windows form (parent) that takes a value from textbox and then opens child form which then uses that value to select an image from a directory. When multiple images are found for the particular value, I have the form modified to display a couple of buttons to navigate (Next & Previous) to display the different images. Upon first opening the parent form, entering a value and then using form.show() to display the child form – everything works as expected. But if another value is entered into parent form (child form can still be open or exited (hidden)) and the ‘Next’ button is clicked the code in the click event is running over again for however many number of images are in the List(imagesFound). Say I have 3 images in the List(imagesFound) and I step through the code in debug mode the btnNext click event fires 3 times in a row. This of course runs GetMultiImages method which causes sequence of displaying the images to be all of. And again, this doesn’t happen the first time a value is entered into parent form. I’ve made sure the list and other variables are cleared out in GetImage method. I’m stumped…any ideas?
Form1:
public partial class Form1 : Form
{
private string parcelID;
Form2 viewer = new Form2();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
parcelID = txtParID.Text;
ShowViewer();
}
private void ShowViewer()
{
viewer.GetImage(parcelID);
if (viewer.NoImageFound == true)
{
viewer.Show();
viewer.Focus();
}
else if (viewer.NoImageFound == false)
{
viewer.Hide();
}
}
}
Child Form:
public partial class Form2 : Form
{
public Button btnNext = new Button();
public Button btnPrev = new Button();
private List<string> imagesFound = new List<string>();
private string Path;
private string parcel;
private int increment;
private int maxNum;
public bool NoImageFound;
//multi image members
private string firstMultiItem;
private string selectMultiImage;
Image parMultiImage;
public Form2()
{
InitializeComponent();
}
public void GetImage(string ParcelID)
{
NoImageFound = true;
parcel = ParcelID;
increment = 0;
maxNum = 0;
firstMultiItem = null;
selectMultiImage = null;
parMultiImage = null;
imagesFound.Clear();
Path = "........\\Images\\";
try
{
if (!string.IsNullOrEmpty(parcel))
{
string parcelTrim = parcel.Substring(0, 6);
Path = Path + parcelTrim + "\\";
foreach (string s in Directory.GetFiles(Path, parcel + "_" + "*"))
{
string trimString = s.Replace(Path, "");
imagesFound.Add(trimString);
}
if ((imagesFound.Count == 0))
{
MessageBox.Show("No images found for ParcelID: " + parcel);
picBox.Image = null;
this.Text = "";
NoImageFound = false;
}
else
{
if (imagesFound.Count == 1)
{
string firstItem = imagesFound[0].ToString();
string selectImage = Path + firstItem;
Image parImage = Image.FromFile(selectImage);
//in order to access the picture box control you have to change it's
//access modifier (Modifier) from private to public. Defaults to private
picBox.Image = parImage;
picBox.SizeMode = PictureBoxSizeMode.StretchImage;
this.Text = parcel;
SingleForm();
}
else if (imagesFound.Count > 1)
{
firstMultiItem = imagesFound[0].ToString();
maxNum = imagesFound.Count;
selectMultiImage = Path + firstMultiItem;
parMultiImage = Image.FromFile(selectMultiImage);
picBox.Image = parMultiImage;
picBox.SizeMode = PictureBoxSizeMode.StretchImage;
this.Text = parcel;
MultiImageForm();
}
}
}
else
{
MessageBox.Show("No ParcelID");
}
}
catch (DirectoryNotFoundException)
{
string text = parcel;
MessageBox.Show("ParcelID: " + text + " could not be found. The directory may be missing.", "There's a problem locating the image.",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void SingleForm()
{
this.Height = 400;
btnNext.Visible = false;
btnPrev.Visible = false;
}
private void MultiImageForm()
{
//set form properties
this.Text = parcel;
this.Height = 432;
//set btnNext properties
btnNext.Location = new Point(307, 375);
btnNext.Size = new Size(75, 25);
btnNext.Font = new Font("Maiandra GD", 10, FontStyle.Bold);
btnNext.Text = ">>";
//add btnNext to form
this.Controls.Add(btnNext);
btnNext.Visible = true;
btnNext.Enabled = true;
//creating event handler for btnNext
btnNext.Click += new EventHandler(btnNext_Click);
//set btnPrev properties
btnPrev.Location = new Point(12, 375);
btnPrev.Size = new Size(75, 25);
btnPrev.Font = new Font("Maiandra GD", 10, FontStyle.Bold);
btnPrev.Text = "<<";
//add btnPrev to form
this.Controls.Add(btnPrev);
btnPrev.Visible = true;
btnPrev.Enabled = false;
//creating event handler for btnPrev
btnPrev.Click += new EventHandler(btnPrev_Click);
}
private void GetMultiImages()
{
try
{
firstMultiItem = imagesFound[increment].ToString();
selectMultiImage = Path + firstMultiItem;
parMultiImage = Image.FromFile(selectMultiImage);
picBox.Image = parMultiImage;
picBox.SizeMode = PictureBoxSizeMode.StretchImage;
}
catch (IndexOutOfRangeException)
{
MessageBox.Show("Index was out of range.");
}
}
private void btnNext_Click(object sender, System.EventArgs e)
{
if (increment != maxNum - 1)
{
increment++;
GetMultiImages();
}
EnableButtons();
}
private void btnPrev_Click(object sender, System.EventArgs e)
{
if (increment > 0)
{
increment--;
GetMultiImages();
}
EnableButtons();
}
private void EnableButtons()
{
if (increment == 0)
{
btnPrev.Enabled = false;
btnNext.Enabled = true;
}
else if (increment > 0 & increment != maxNum - 1)
{
btnPrev.Enabled = true;
btnNext.Enabled = true;
}
else if (increment == maxNum - 1)
{
btnPrev.Enabled = true;
btnNext.Enabled = false;
}
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
//overriding closing event
this.Hide();
e.Cancel = true;
}
}
//creating event handler for btnNext
btnNext.Click += new EventHandler(btnNext_Click);
That's a bug. You keep adding a Click event handler for the button, each time you call MultiImageForm(). So the event handler runs multiple times for a single click.
Only add event handlers in the form constructor so you can be sure it is only done once.

unable to set Row.Readonly=false in Datagridview in winforms

I have a datagridview with its ReadOnly set true to prevent people editing.
then I have a button on each row. when I click on a specific button, I wrote:
private void DGGrade_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex > 0 && e.ColumnIndex == DGGrade.Columns["Edit"].Index)
{
DGGrade.Rows[DGGrade.CurrentCell.RowIndex].ReadOnly = false;
DGGrade.Rows[DGGrade.CurrentCell.RowIndex].DefaultCellStyle.BackColor = Color.White;
}
But it is not working. please help
I do not know the reason why it is not working but as far as i can tell from my test runs it has to deal how the data is bound. If you use dataGridView1.DataSource = GetDataSource(); then it did not work in my tests. I have read once about some of the drawbacks of automated binding but i could not find it. Here is the code that works. A row is only in EditMode after the User has clicked the button Edit in the corresponding row. I will be back later - let me know if you need more pointers.
public partial class Form1 : Form
{
int rowIndexOfEditableRow = -1;
public Form1() {
InitializeComponent();
CreateDataGridView(dataGridView1);
SetExistingDataGridViewRowsReadOnly();
this.dataGridView1.Columns.Add(GetBtnColumn());
}
private void SetExistingDataGridViewRowsReadOnly() {
DataGridViewRowCollection rows = this.dataGridView1.Rows;
foreach (DataGridViewRow row in rows) {
row.ReadOnly = true;
}
}
It seems that the grid must be filled manually - at least this way the change of ReadOnly works
public void CreateDataGridView(DataGridView dgv)
{
dgv.ColumnCount = 3;
dgv.Columns[0].Name = "Id";
dgv.Columns[1].Name = "Lastname";
dgv.Columns[2].Name = "City";
dgv.BackgroundColor = Color.Gray;
AddRowsToDataGridView(dgv);
}
private void AddRowsToDataGridView(DataGridView dgv)
{
string[] row1 = new string[]{"1", "Muller", "Seattle"};
string[] row2 = new string[]{"2", "Arkan", "Austin"};
string[] row3 = new string[]{"3", "Cooper", "New York"};
object[] rows = new object[] { row1, row2, row3 };
foreach (string[] rowArray in rows)
{
dgv.Rows.Add(rowArray);
}
}
Helper method to create a column with a button
public DataGridViewButtonColumn GetBtnColumn()
{
DataGridViewButtonColumn btnColumn = new DataGridViewButtonColumn();
btnColumn.HeaderText = "Edit";
btnColumn.Text = "Edit";
btnColumn.UseColumnTextForButtonValue = true;
return btnColumn;
}
Event handler checks if the user has clicked the edit button. In this case the current row will be set to ReadOnly = false. This allows that the user to edit the row. To emphasize it i changed the background color of the row.
private void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
int colIndex = e.ColumnIndex;
int rowIndex = e.RowIndex;
Type cellType = dataGridView1.Columns[colIndex].CellType;
if (cellType == typeof(DataGridViewButtonCell))
{
dataGridView1.Rows[rowIndex].ReadOnly = false;
dataGridView1.Rows[rowIndex].DefaultCellStyle.BackColor = Color.GreenYellow;
this.rowIndexOfEditableRow = rowIndex;
label1.Text = rowIndexOfEditableRow.ToString() + " " + colIndex.ToString();
}
}
If the Row-leave-Event is fired the style is reset and the global parameter which row is editable is set to the initial value
private void DataGridView1_RowLeave(object sender, DataGridViewCellEventArgs e)
{
int rowIndex = e.RowIndex;
dataGridView1.Rows[rowIndex].ReadOnly = true;
dataGridView1.Rows[rowIndex].DefaultCellStyle.BackColor = Color.White;
this.rowIndexOfEditableRow = -1;
}
}
The above code contains all (except the designer files) that you need to create this demo:

RepositoryItemCheckEdit doesn't stay checked

I try to add a RepositoryItemCheckEdit to my GridView using devexpress and Winforms. However, I can get only one checkbox be checked. If I check another one, the checkbox I checked before becomes unchecked. I followed everything I can find on the net, but couldn't make this work. What am I missing?
The code part I insert the column:
gcIsEmirleri.DataSource = (from i in isemirleri
select new
{
ID = i.isEmriId,
// other attributes
}).ToList();
GridColumn column = gvIsEmirleri.Columns["Sec"];
if (column == null)
{
gvIsEmirleri.BeginUpdate();
DataColumn col = new DataColumn("Sec", typeof(bool));
column = gvIsEmirleri.Columns.AddVisible("Sec");
col.VisibleIndex = 0;
col.Caption = "Sec";
col.Name = "Sec";
col.OptionsColumn.AllowEdit = true;
gvIsEmirleri.EndUpdate();
gvIsEmirleri.Columns["Sec"].UnboundType = DevExpress.Data.UnboundColumnType.Boolean;
RepositoryItemCheckEdit chk = new RepositoryItemCheckEdit();
chk.ValueChecked = true;
chk.ValueUnchecked = false;
gvIsEmirleri.Columns["Sec"].ColumnEdit = chk;
chk.QueryCheckStateByValue += chk_QueryCheckStateByValue;
}
The code part I make the checkbox two-stated instead of three:
private void chk_QueryCheckStateByValue(object sender, DevExpress.XtraEditors.Controls.QueryCheckStateByValueEventArgs e)
{
if (e.Value == null)
{
e.CheckState = CheckState.Unchecked;
e.Handled = true;
}
}
EDIT: I created a List<bool> chkList; and do the following operations:
This function is added to checkedits' CheckStateChanged:
private void chk_CheckStateChanged(object sender, EventArgs e)
{
CheckEdit chk = sender as CheckEdit;
if (chk.Checked)
chkList[gvIsEmirleri.FocusedRowHandle] = true;
else
chkList[gvIsEmirleri.FocusedRowHandle] = false;
FillBindingSource();
}
In FillBindingSource I added the lines:
for (int i = 0; i < chkList.Count; i++)
{
if (chkList[i])
gvIsEmirleri.SetRowCellValue(i, "Sec", true);
}
I debug these lines, I see that List has correct bool values and gvIsEmirleri.SetRowCellValue(i, "Sec", true); is operated when it has to. However, it still doesn't work.
My guess is : You are using an unbound Column, and you are not saving the checked / unckecked info, so, after the selected row is left, the checkBox get it's initial value (unckecked).
For this, I suggest you handle the CustomUnboundColumnData event of your view. Here is a simple :
readonly Dictionary<object, bool> checkedMap = new Dictionary<object, bool>();
private void viewScales_CustomUnboundColumnData(object sender, CustomColumnDataEventArgs e)
{
// Check what column
if (e.Column != gvIsEmirleri.Columns["Sec"])
return;
if (e.IsGetData)
{
// check if the row has been checked and set it's value using e.Value
bool checked;
if (checkedMap.TryGetValue(e.Row, out checked))
e.Value = checked;
}
if (e.IsSetData)
{
var checked = Convert.ToBoolean(e.Value);
// Check if the key already exist
if (checkedMap.ContainsKey(e.Row))
scaleMap.Remove(e.Row);
checkedMap.Add(e.Row, checked);
}
}
Note : This is the way I resolved a similar problem, but I did not test the code I just wrote.

DataGrid select last cell programmatically

When the DataGrid below gets the focus for the first time and only the first time (ie, after some other control has had the focus), the last row, 2nd column should be focused and in edit.
I added a handler for the DataGrid.GotFocus, but it's complicated code and not getting the result above.
Anyone got an elegant, bullet proof solution?
I made tiny modifications to the code
the sender should always be the grid I want, so I just used that instead of relying on a name
When the SelectionUnit is FullRow, as my grid was before I changed it to CellOrRowHeader you
apparently can't call SelectedCells.Clear()
Code below:
private void OnDataGridKeyboardGotFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var dg = sender as DataGrid;
if (_hasHadInitialFocus) return;
var rowIndex = dg.Items.Count - 2;
if (rowIndex >= 0 && dg.Columns.Count - 1 >= 0)
{
var column = dg.Columns[dg.Columns.Count - 1];
var item = dg.Items[rowIndex];
var dataGridCellInfo = new DataGridCellInfo(item, column);
if (dg.SelectionUnit != DataGridSelectionUnit.FullRow) {
dg.SelectedCells.Clear();
dg.SelectedCells.Add(dataGridCellInfo);
}
else {
var row = dg.GetRow(rowIndex);
row.IsSelected = true;
}
dg.CurrentCell = dataGridCellInfo;
dg.BeginEdit();
}
_hasHadInitialFocus = true;
}
New Question
I want to repeat the selection when the focus goes to another control in the window and then back to the grid.
I thought I could turn that _hasHadInitialFocus latch to false in a LostFocus event, but the code below is firing on cell changes.
Do you know how I should be trapping the lost focus event better, and do you agree that is the place to turn the latch off?
private void DataGridLostFocus(object sender, RoutedEventArgs e) {
_hasHadInitialFocus = false;
}
You may have to fiddle with the offsets depending on whether there's an new item row visible or not, but this works for me.
private bool _hasHadInitialFocus;
private void DataGridGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!_hasHadInitialFocus)
{
if (dataGrid.Items.Count - 2 >= 0 && dataGrid.Columns.Count - 1 >= 0)
{
var dataGridCellInfo = new DataGridCellInfo(
dataGrid.Items[dataGrid.Items.Count - 2], dataGrid.Columns[dataGrid.Columns.Count - 1]);
dataGrid.SelectedCells.Clear();
dataGrid.SelectedCells.Add(dataGridCellInfo);
dataGrid.CurrentCell = dataGridCellInfo;
dataGrid.BeginEdit();
}
_hasHadInitialFocus = true;
}
}
I noticed that clicking into the grid leaves one cell selected and the target cell in edit mode. A solution to this if required is:
private void DataGridGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
EditCell();
}
private void PreviewMouseLBDown(object sender, MouseButtonEventArgs e)
{
if (!_hasHadInitialFocus)
{
e.Handled = true;
EditCell();
}
}
private void EditCell()
{
if (!_hasHadInitialFocus)
{
if (dataGrid.Items.Count - 2 >= 0 && dataGrid.Columns.Count - 1 >= 0)
{
var dataGridCellInfo = new DataGridCellInfo(
dataGrid.Items[dataGrid.Items.Count - 2], dataGrid.Columns[dataGrid.Columns.Count - 1]);
dataGrid.SelectedCells.Clear();
dataGrid.SelectedCells.Add(dataGridCellInfo);
dataGrid.CurrentCell = dataGridCellInfo;
dataGrid.BeginEdit();
}
_hasHadInitialFocus = true;
}
}

Silverlight Mouse Events: MouseEnter and MouseLeave conflicts

I have a collection of buttons in a grid. For each one of these buttons, I want to handle the MouseEnter and MouseLeave events to animate the height of the button (and do some other interesting stuff). It all works good until I start moving my mouse too fast over and off the buttons which eventually cause the events to take place at before the other is complete. What's the best way of making sure the events wait for eachother before being triggered?
UPDATE:
Going by x0r's advice, I refactored my code into an internal class which inherits from Button and has the required methods to perform the animations. Unfortunately, this did not really solve the problem because - I think - I'm handling the Completed event of the first animation in two separate places. (correct me if I'm wrong). Here's my code:
internal class MockButton : Button
{
#region Fields
private Storyboard _mouseEnterStoryBoard;
private Storyboard _mouseLeaveStoryBoard;
private Double _width;
#endregion
#region Properties
internal Int32 Index { get; private set; }
#endregion
#region Ctors
internal MockButton(Int32 index) : this(index, 200)
{
}
internal MockButton(Int32 index, Double width)
{
this.Index = index;
this._width = width;
}
#endregion
#region Event Handlers
internal void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if (_mouseEnterStoryBoard == null)
{
_mouseEnterStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.From = 10;
heightAnimation.To = targetAnimationHeight;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseEnterStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseEnterStoryBoard.Children.Add(heightAnimation);
}
_mouseEnterStoryBoard.Completed += (s, e) =>
{
action.Invoke();
};
_mouseEnterStoryBoard.Begin();
}
internal void OnMouseLeave()
{
if (_mouseLeaveStoryBoard == null)
{
_mouseLeaveStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.To = 10;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseLeaveStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseLeaveStoryBoard.Children.Add(heightAnimation);
}
if (_mouseEnterStoryBoard.GetCurrentState() != ClockState.Stopped)
{
_mouseEnterStoryBoard.Completed += (s, e) =>
{
_mouseLeaveStoryBoard.Begin();
};
}
else
{
_mouseLeaveStoryBoard.Begin();
}
}
#endregion
}
UPDATE 2:
Some events are getting triggered multiple times. An example of that is the Click event on the close button of my Rule object...
public Rule(Action<Int32> closeAction)
{
this.Style = Application.Current.Resources["RuleDefaultStyle"] as Style;
this.CloseAction = closeAction;
this.Loaded += (s, e) =>
{
if (_closeButton != null)
{
_closeButton.Click += (btn, args) =>
{
if (this.CloseAction != null)
{
this.CloseAction.Invoke(this.Index);
}
};
if (_closeButtonShouldBeVisible)
{
_closeButton.Visibility = System.Windows.Visibility.Visible;
}
}
};
}
And below is the Action<Int32> I'm passing to the Rule object as the CloseAction:
private void RemoveRule(Int32 ruleIndex)
{
Rule ruleToRemove = Rules.FirstOrDefault(x => x.Index.Equals(ruleIndex));
Storyboard sb = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
sb.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Opacity"));
animation.Duration = TimeSpan.FromMilliseconds(300);
animation.From = 1;
animation.To = 0;
sb.Children.Add(animation);
Storyboard.SetTarget(animation, ruleToRemove);
sb.Completed += (s, e) =>
{
if (Rules.FirstOrDefault(x => x.Index.Equals(ruleIndex)) != null)
{
this.Rules.RemoveAt(ruleIndex);
}
};
sb.Begin();
}
UPDATE 3:
In order to avoid the animations running too early, I thought I could delay the MouseEnter event, so if the user just scrolls over the item too fast, it doesn't kick off. But I have a problem now: Say the user mouses over the item and then mouses out. If I use the Storyboard.BeginTime property, that won't safe guard against that behavior because eventhough the animation gets delayed, it's still going to start eventually... So is there a way I could prevent that from happening?
Any suggestions?
check in your mouseleave eventhandler if the first storyboard is still running and if that is the case attach the starting of the second storyboard to the Completed event of the first storybaord:
private void OnOddRowMouseLeave(object sender, MouseEventArgs e)
{
...
if(_firstStoryboard.GetCurrentState() != ClockState.Stopped)
_firstStoryboard.Completed += (s,e) => _secondStoryboard.Begin();
else
_secondStoryboard.Begin()
Everything that Silverlight does is asyncronous and so most likely what is happening is that because you are moving quickly in and out of the box the mouse leave is being fired before the mouseenter has a chance to finish. You could setup your two events so thay they have an indicator of whether or not the other is in process. For example you could do this
bool mouseOut =false;
bool mouseIn =false;
void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if(!this.mouseOut)
{
this.mouseIn = true;
if (_mouseEnterStoryBoard == null)
{
_mouseEnterStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.From = 10;
heightAnimation.To = targetAnimationHeight;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseEnterStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseEnterStoryBoard.Children.Add(heightAnimation);
}
_mouseEnterStoryBoard.Completed += (s, e) =>
{
action.Invoke();
};
_mouseEnterStoryBoard.Begin();
if(this.mouseOut)
{
this.OnMouseLeave();
}
this.mouseIn = false;
}
}
void OnMouseLeave()
{
if(!this.mouseIn)
{
this.mouseOut = false;
if (_mouseLeaveStoryBoard == null)
{
_mouseLeaveStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.To = 10;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseLeaveStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseLeaveStoryBoard.Children.Add(heightAnimation);
}
if (_mouseEnterStoryBoard.GetCurrentState() != ClockState.Stopped)
{
_mouseEnterStoryBoard.Completed += (s, e) =>
{
_mouseLeaveStoryBoard.Begin();
};
}
else
{
_mouseLeaveStoryBoard.Begin();
}
}
else
{
this.mouseOut = true;
}
}
I haven't actually checked this code but this should help you to at least get closer to what you want. This should be quick enough that your user doesn't realize that it is not firing exactly on exit if they go over it quickly. But this should help to keep you from getting overlap.
Another way you could do this is setup the initial events as null, and have the mouse in event set the mouse in event when it is complete but the problem with that is that if the mouse out fires before the event is set then you don't event get the event firing.

Resources