I am creating a WPF application which has a MediaElement that plays video. I didn't like the way the Slider control looked. I therefore I attempted, mostly successfully to use a ProgressBar in its place.
I have implemented functionality for clicking the ProgressBar to set the value of both itself and that of the position of the MediaElement. However when I also implemented a DispatcherTimer to increment the value of the ProgressBar alongside this functionality I am getting an oddity that I cannot work out where it is coming from.
Basically when I click on the ProgressBar, the fill correctly updates to where the cursor is (I am setting it directly in setProgressBarPosition), but then moves back a bit (I am guessing, when the _Tick method call is made of the DispatcherTimer, which is setting the ProgressBar.Value based on the position of meVideo).
The MSPaint arrow shows where I clicked, the red fill shows where it updated to, the gap between the cursor click and the position of the fill gets larger the later in the timeline I click which makes me think I have made a mistake in a calculation somewhere:
I am thinking I have probably done something wrong in the setting of meVideo.Position in pgbVideo_MouseUp but I can't see it. Here is my code:
private void videoTimer_Tick(object sender, EventArgs e)
{
if (!isDragging)
{
pgbVideo.Value = meVideo.Position.TotalSeconds;
}
}
/*
* Handles the clicking of the video timeline.
*/
private void pgbVideo_MouseDown(object sender, MouseButtonEventArgs e)
{
double mousePosition = e.GetPosition(pgbVideo).X;
setProgressBarPosition(mousePosition);
isDragging = true;
}
/*
* Handles the dragging of the mouse over the video timeline.
*/
private void pgbVideo_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
double mousePosition = e.GetPosition(pgbVideo).X;
setProgressBarPosition(mousePosition);
}
}
/*
* Sets the position of the video timeline progressbar.
*/
private void setProgressBarPosition(double mousePosition)
{
if (meVideo.Source != null)
{
double progressBarPosition = mousePosition / pgbVideo.ActualWidth * pgbVideo.Maximum;
pgbVideo.Value = progressBarPosition;
}
}
/*
* Sets the position of the media element.
*/
private void pgbVideo_MouseUp(object sender, MouseButtonEventArgs e)
{
if (meVideo.Source != null)
{
double videoPositon = meVideo.NaturalDuration.TimeSpan.Ticks * (pgbVideo.Value / pgbTime.Maximum);
meVideo.Position = new TimeSpan((int)videoPositon);
}
isDragging = false;
}
Thanks for reading, any help appreciated!
EDIT: Here is my _MediaOpened method where I set the maximum value of the ProgressBar:
private void meVideo_MediaOpened(object sender, RoutedEventArgs e)
{
pgbVideo.Maximum = meVideo.NaturalDuration.TimeSpan.TotalSeconds;
videoTimer.Start();
isPlaying = true;
}
Annoyingly it was a simple mistake made by me being too quick on the IntelliSense! In pgbVideo_MouseUp the line:
double videoPositon = meVideo.NaturalDuration.TimeSpan.Ticks * (pgbVideo.Value / pgbTime.Maximum);
Should be:
double videoPositon = meVideo.NaturalDuration.TimeSpan.Ticks * (pgbVideo.Value / pgbVideo.Maximum);
pgbTime refers to another, unrelated ProgressBar which has the Maximum of 100. With the TotalSeconds of the loaded video being used to set the Maximum of pgbVideo and the video length being 92 seconds, the problem presented as an issue with accuracy (to me at least!), whereas if the video was 10 minutes the behaviour would have been way off and maybe led me to looking for a more obvious mistake!
Thanks to everyone that tried to help.
Related
So i tried to make a button more animated by making that when i hover my mouse over the button its back colour goes slowly from a darker gray to lighter gray, sadly the the MouseHover didn't worked out great for me because i had to use it with if (while, do and for isn't working at all) so i changed it to MouseMove And well that created its own problems, right now the colour getting lighter only when i move my mouse on it, here is the code:
private void BtnPlay_MouseMove(object sender, MouseEventArgs e)
{
byte coloDat = btnPlay.FlatAppearance.MouseOverBackColor.B;
if (btnPlay.FlatAppearance.MouseOverBackColor != Color.FromArgb(68, 68, 68))
{
coloDat++;
btnPlay.FlatAppearance.MouseOverBackColor = Color.FromArgb(coloDat, coloDat, coloDat);
System.Threading.Thread.Sleep(1);
}
}
Im gonna use the code multiple times in the project so is there a way to do this without making a wall of text?
Edit: For avoiding confusion; im trying to do my project with Button.FlatAppearance.MouseOverBackColor and not Button.BackgroundColour.
If you want to create this in WPF, just create a style with a storyboard. In windows forms, you need to use a timer.
The reason a loop didn't work for you is that the loop was running on the same thread as the UI, so the UI wasn't being updated until after your loop was over. To animate an effect in windows forms, you have to let the event function end so that the UI can update and then have your function called again for the next frame. That is what the timer element does.
I created a demo program with two animated buttons. To create buttons with animated background colors I first set up a start color, end color, the amount I want the color to change in each frame, the button the mouse is currently over and the transition progress of each button. I added that last so that I could have the buttons gradually transition back after the mouse was over something else.
private Color startColor = Color.AliceBlue;
private Color endColor = Color.BlueViolet;
private double step = 0.01;
private Button lastOver = null;
private Dictionary<Button, double> transitionProgress = new Dictionary<Button, double>();
Then I attached the event handlers of both of my buttons to the same functions, the functions below. The first uses ContainsKey so that I can make more buttons animated by just assigning them to these event handler functions.
private void demoButton_MouseHover(object sender, EventArgs e)
{
if (sender != lastOver)
{
lastOver = (Button)sender;
if (!transitionProgress.ContainsKey(lastOver))
{
transitionProgress[lastOver] = 0.0;
}
}
}
private void demoButton_MouseLeave(object sender, EventArgs e)
{
lastOver = null;
}
Then I created a Timer with the following event handler. It goes through each button and transitions it based on whether the mouse is currently over that button. It also only updates the background color if it has changed to improve performance.
private void styleUpdate_Tick(object sender, EventArgs e)
{
for (int i = 0; i < transitionProgress.Count; i++)
{
Button button = transitionProgress.Keys.ElementAt(i);
bool changing = false;
if (button == lastOver)
{
if (transitionProgress[button] < 1.0)
{
transitionProgress[button] = Math.Min(1.0, transitionProgress[button] + step);
changing = true;
}
}
else
{
if (transitionProgress[button] > 0.0)
{
transitionProgress[button] = Math.Max(0.0, transitionProgress[button] - step);
changing = true;
}
}
if (changing)
{
double progress = transitionProgress[button];
button.BackColor = Color.FromArgb(
(int)Math.Floor((endColor.R - startColor.R) * progress + startColor.R),
(int)Math.Floor((endColor.G - startColor.G) * progress + startColor.G),
(int)Math.Floor((endColor.B - startColor.B) * progress + startColor.B)
);
}
}
}
The timer has to be enabled and the interval set to 16
this.styleUpdate.Enabled = true;
this.styleUpdate.Interval = 16;
this.styleUpdate.Tick += new System.EventHandler(this.styleUpdate_Tick);
That does seem like a lot of code, but to add it to another button, you just need two more lines of code.
this.yourButtonName.MouseLeave += new System.EventHandler(this.demoButton_MouseLeave);
this.yourButtonName.MouseHover += new System.EventHandler(this.demoButton_MouseHover);
I was trying to change the back color of a DevExpress TileView when the tileview has been checked. However, it didn't change the color even if the line has been executed. So what should I do to make it happen? Here's my current implementation.
private void tileViewWaves_ItemCustomize(object sender, TileViewItemCustomizeEventArgs e)
{
// get wave model DTO for tile
var wave = tileViewWaves.GetRow(e.RowHandle) as dtoReferenceWave;
// display tile as checked if it is ready for all wave
if (wave.frequency != 0 && wave.amplitude != 0)
{
e.Item.Checked = _presenter.WaveHasAllReference(wave) && _presenter.SufficientReference;
e.Item.Appearance.BackColor = Color.Green;
Console.WriteLine($"INFO: Waves have been completed populated and checked, should turn green now with {e}");
}
}
I figured out a way to do this, but it is a hack from devexpress.
So I set two colors during the Loading phase:
private void frmCalibration_Load(object sender, EventArgs e)
{
// Set colors.
tileViewWaves.Appearance.ItemNormal.BackColor = _controlWaveColor;
tileViewWaves.Appearance.ItemFocused.BackColor = _selectedWavePointColor;
tileViewWaves.Click += tileViewWaves_Click;
}
and if the condition that I've set is met, it will be highlighted by the default devexpress settings.
We use....
_tileItem.AppearanceItem.Normal.BackColor = BackColor;
Whenever I move a Windows Form by some component (i.e. a Label) in the client area, I end up with a strange mouse offset in which the form does not stay visually underneath the cursor. It will still move according to my mouse location on the screen, but it dramatically shifts southeast of the cursor's position.
I've had to specify a negative offset of my own to counteract this offset; my code is as follows:
private void component_MouseDown(object sender, MouseEventArgs e)
{
if (sender is Label)
{
if (e.Button == MouseButtons.Left)
{
mouseLoc = new Point(-(e.X + OFFSET_X), -(e.Y + OFFSET_Y));
isMouseDown = true;
}
}
}
private void component_MouseMove(object sender, MouseEventArgs e)
{
if (isTitleLabelMouseDown)
{
Point p = Control.MousePosition;
p.Offset(mouseLoc);
Location = p;
}
}
private void component_MouseUp(object sender, MouseEventArgs e)
{
isMouseDown = false;
}
This offset does fix the problem, but what throws me for a loop is why the form's location offsets when I move it by its client area in the first place?
Thanks!
You seem to be translating client coordinates to screen coordinates. There is a better way...
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.pointtoscreen%28v=vs.110%29.aspx
Edit: And of course there's a better way to do this whole thing. Basically, you want to intercept the click higher up the chain and tell Windows that the click is actually in the window title, which will cause Windows to do the dragging for you...
Winforms - WM_NCHITEST message for click on control
I'm trying to move an object according to the direction buttons Up,Left,Right,Down.
I am setting the margin property like:-
img.Margin = new Thickness(l, t, r, b); //L T R B
I am incrementing/decrementing the values according to the desired movement needed.
I am able to move the object through the click event. However, I'd like to move the object in the desired direction whenever the button is pressed and held for the user. As soon as the user releases the button the movement should also stop.
I tried using the hold event, but the operation executed once and then stopped.
On another attempt I tried looping my statements but the App stalled.
Kindly help me out. Thanks!
EDIT:-
I handled the ManipulationStarted,ManipulationDelta,ManipulationCompleted events.
Now, I'm able to move my object whenever I'm pressing and holding the button. However, the new problem that I'm facing is that I have to constantly keep my finger moving on the screen so as to perform the motion.
The code for the Up Button(the button that moves the object in the vertical direction) is:-
public double l = 0.0, t = 0.0, r = 0.0, b = 0.0;
public void move()
{
img.Margin = new Thickness(l, t, r, b); //L T R B
}
private void up_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
}
private void up_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
t = t + 1.0;
move();
}
private void up_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
}
I'm not sure whether this method is correct or not. Do advise. Thanks.
You should use ManipulationStarted and ManipulationCompleted events. They works for Tap and Hold gestures as stated here: https://msdn.microsoft.com/en-us/library/windows/apps/ff426933(v=vs.105).aspx
Update
To correctly detect the beginning and the end of a tap, I suggest using MouseEnterand MouseLeaving events.
Here a sample which shows how I'm moving down an object.
This is currently a square in the centre of the screen:
<Grid x:Name="objectToMove" Background="red" Height="100" Width="100">
<Grid.RenderTransform>
<TranslateTransform x:Name="verticalTransform" Y="0" />
</Grid.RenderTransform>
</Grid>
The code behind:
AutoResetEvent autoEvent = new AutoResetEvent(true);
System.Threading.Timer dt;
private void toggle_MouseEnter(object sender, MouseEventArgs e)
{
dt = new System.Threading.Timer(new TimerCallback(MoveFunct), autoEvent, 0, 1);
}
private void MoveFunct(Object stateInfo)
{
Deployment.Current.Dispatcher.BeginInvoke(() => { verticalTransform.Y += 3; });
}
private void toggle_MouseLeave(object sender, MouseEventArgs e)
{
dt.Dispose();
}
Note that the last parameter of Timer constructor consists in the interval between ticks. Also inside MoveFunct function, I'm calling a Dispatcher method otherwise it could not access UI thread.
In the sample I used a TranslateTransform which is better, in my opinion, than Margin manipulation because it requires to update the element whole visual tree.
I am writing a screensaver program in C#. And I want it to do the following:
Start with a text. After about 3 seconds or more, hide the text and show an image. After the next 3 seconds, hide the image show the text and keep going round the loop until the user does something that exits the screensaver.
What I have done:
I started with a simple textlabel and a timer control. I was able to get the textlabel change positions on screen after every 3 seconds. I updated my code to include a picturebox, and in my timer_tick method, I inserted an if-else statement to check, when the method is called,
if the textlabel is shown, hide it and show the picturebox.
else if the picturebox is shown, hide it and show the textbox. Code is shown below:
private void Form1_Load(object sender, EventArgs e)
{
Cursor.Hide();
TopMost = true;
moveTimer.Interval = 3000;
moveTimer.Tick += new EventHandler(moveTimer_Tick);
moveTimer.Start();
}
private void moveTimer_Tick(object sender, System.EventArgs e)
{
//Move text to new Location
//textLabel.Left = rand.Next(Math.Max(1, Bounds.Width - textLabel.Width));
//textLabel.Top = rand.Next(Math.Max(1, Bounds.Height - textLabel.Height));
if (pictureBox1.Enabled == true)
{
pictureBox1.Hide();
textLabel.Show();
}
if (textLabel.Enabled == true)
{
textLabel.Hide();
pictureBox1.Show();
}
}
Here's the problem:
WHen I run the screensaver program, the screen starts with the text, changes to the picture after 3 seconds and stops there.
What do I do to get it moving in a continous loop, showing/hiding the textlabel or picturebox?
Have I implemented this in the right way?
Please clear and concise explanations/answers will be highly appreciated.
Thanks!
Perhaps you could keep the state in a variable that you can switch
private bool state = false;
private void moveTimer_Tick(object sender, System.EventArgs e)
{
//Move text to new Location
//textLabel.Left = rand.Next(Math.Max(1, Bounds.Width - textLabel.Width));
//textLabel.Top = rand.Next(Math.Max(1, Bounds.Height - textLabel.Height));
if (state)
{
pictureBox1.Hide();
textLabel.Show();
}
else
{
textLabel.Hide();
pictureBox1.Show();
}
state = !state;
}
How about something like this?
Enabled says whether the object can receive input.
Visible is what says if its visible or not.
You see it change once and only once because all the objects are enabled. The first if succeeds, hiding the picture and showing the text. But then the second if also succeeds, showing the picture and hiding the text. Since this is all in one event callback, you never see the first if happen, because the second overrides it.
As you're realizing in the comments, the answer is to not check enabled. Instead, check Visible. Make sure to use an else as well, otherwise you may still get the same issue of both being true.
private void moveTimer_Tick(object sender, System.EventArgs e)
{
//Move text to new Location
//textLabel.Left = rand.Next(Math.Max(1, Bounds.Width - textLabel.Width));
//textLabel.Top = rand.Next(Math.Max(1, Bounds.Height - textLabel.Height));
if (pictureBox1.Visible == true)
{
pictureBox1.Hide();
textLabel.Show();
}
else
{
textLabel.Hide();
pictureBox1.Show();
}
}