Determine when LongListSelector is Scrolling - c#

I would just simply like to be able to minimize the application bar when I am scrolling down, and then show its normal size when scrolling up. I've seen this ability on the facebook app and it seems very appealing and user friendly. I have my LongListSelector with items bound to it, and an appbar already in code behind. What is the missing key to enable such a feature?

You just need to figure out when the user is scrolling and in what direction. Here's a great article with example code. Detecting WP8 LongListSelector’s end of scroll. You can modify it to the point where it does exactly what you want.
However, if I was going do it, I would take a more direct route. I would derived my own LLS and attach a property to the value of the scrollbar. Something like this :)
public class MyLLS : LongListSelector, INotifyPropertyChanged
{
// implement the INotify
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// dat grab doe
sb = this.GetTemplateChild("VerticalScrollBar") as System.Windows.Controls.Primitives.ScrollBar;
sb.ValueChanged += sb_ValueChanged;
}
void sb_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
// an animation has happen and they have moved a significance distance
// set the new value
ScrollValue = e.NewValue;
// determine scroll direction
if(e.NewValue > e.OldValue)
{
scroll_direction_down = true;
}
else
{
scroll_direction_down = false;
}
}
public System.Windows.Controls.Primitives.ScrollBar sb;
private bool scroll_direction_down = false; // or whatever default you want
public bool ScrollDirectionDown
{ get { return scroll_direction_down; } }
public double ScrollValue
{
get
{
if (sb != null)
{
return sb.Value;
}
else
return 0;
}
set
{
sb.Value = value;
NotifyPropertyChanged("ScrollValue");
}
}
}
Now you know the exact scroll position. You can even get the top and bottom value by doing
double min = this.sb.Minimum;
double max = this.sb.Maximum;
Now bind that ScrollDirectionDown property to a converter to your AppBar visibility and you'll have your goals met.
If you can't bind then you have to do a callback to update the visibility. But if you want something more simple just hook it up to the ManipulationStateChanged event of the custom LLS.
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
private void lls_ManipulationStateChanged(object sender, EventArgs e)
{
if (lls.ScrollDirectionDown)
{
ApplicationBar.IsVisible = false;
}
else
{
ApplicationBar.IsVisible = true;
}
}
}

So for that you have to detect when the longlistselector starts scrolling. For that to achieve there's a similar thread here:
Windows Phone 8 Long List Selector - scroll to bottom after data loaded async
In the DoAndScroll method you could simply include the code to minimize the AppBar.
Within your xaml code of your appbar, change the mode to Minimized.
<shell:ApplicationBar Mode="Minimized" Opacity="1.0" IsMenuEnabled="True" IsVisible="True"></>
Thereafter whenever it scrolls back up, make the Mode of the AppBarto Default.
Or else have a look at this to detect the bottom of the longlistselector.
Detecting WP8 LongListSelector’s end of scroll

Related

Moving methods from view to viewmodel - WPF MVVM

I have the following code in my code behind:
public partial class MainWindow
{
private Track _movieSkipSliderTrack;
private Slider sMovieSkipSlider = null;
private Label lbTimeTooltip = null;
private MediaElement Player = null;
public VideoPlayerViewModel ViewModel
{
get { return DataContext as VideoPlayerViewModel; }
}
public MainWindow()
{
InitializeComponent();
}
private void SMovieSkipSlider_OnLoaded(object sender, RoutedEventArgs e)
{
_movieSkipSliderTrack = (Track)sMovieSkipSlider.Template.FindName("PART_Track", sMovieSkipSlider);
_movieSkipSliderTrack.Thumb.DragDelta += Thumb_DragDelta;
_movieSkipSliderTrack.Thumb.MouseEnter += Thumb_MouseEnter;
}
private void Thumb_MouseEnter(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && e.MouseDevice.Captured == null)
{
var args = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonDownEvent
};
SetPlayerPositionToCursor();
_movieSkipSliderTrack.Thumb.RaiseEvent(args);
}
}
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
SetPlayerPositionToCursor();
}
private void SMovieSkipSlider_OnMouseEnter(object sender, MouseEventArgs e)
{
lbTimeTooltip.Visibility = Visibility.Visible;
lbTimeTooltip.SetLeftMargin(Mouse.GetPosition(sMovieSkipSlider).X);
}
private void SMovieSkipSlider_OnPreviewMouseMove(object sender, MouseEventArgs e)
{
double simulatedPosition = SimulateTrackPosition(e.GetPosition(sMovieSkipSlider), _movieSkipSliderTrack);
lbTimeTooltip.AddToLeftMargin(Mouse.GetPosition(sMovieSkipSlider).X - lbTimeTooltip.Margin.Left + 35);
lbTimeTooltip.Content = TimeSpan.FromSeconds(simulatedPosition);
}
private void SMovieSkipSlider_OnMouseLeave(object sender, MouseEventArgs e)
{
lbTimeTooltip.Visibility = Visibility.Hidden;
}
private void SetPlayerPositionToCursor()
{
Point mousePosition = new Point(Mouse.GetPosition(sMovieSkipSlider).X, 0);
double simulatedValue = SimulateTrackPosition(mousePosition, _movieSkipSliderTrack);
SetNewPlayerPosition(TimeSpan.FromSeconds(simulatedValue));
}
private double CalculateTrackDensity(Track track)
{
double effectivePoints = Math.Max(0, track.Maximum - track.Minimum);
double effectiveLength = track.Orientation == Orientation.Horizontal
? track.ActualWidth - track.Thumb.DesiredSize.Width
: track.ActualHeight - track.Thumb.DesiredSize.Height;
return effectivePoints / effectiveLength;
}
private double SimulateTrackPosition(Point point, Track track)
{
var simulatedPosition = (point.X - track.Thumb.DesiredSize.Width / 2) * CalculateTrackDensity(track);
return Math.Min(Math.Max(simulatedPosition, 0), sMovieSkipSlider.Maximum);
}
private void SetNewPlayerPosition(TimeSpan newPosition)
{
Player.Position = newPosition;
ViewModel.AlignTimersWithSource(Player.Position, Player);
}
}
I would like to follow the MVVM pattern and have this code moved to my ViewModel which at the moment has only few properties. I have read a lot of answer here and outside of StackOverflow on the topic, I've downloaded some github projects to check out how experienced programmers handle specific situations, but none of that seem to clear out the confusion for me. I'd like to see how can my case be refactored to follow the MVVM pattern.
Those are the extra extension methods and also the ViewModel itself:
static class Extensions
{
public static void SetLeftMargin(this FrameworkElement target, double value)
{
target.Margin = new Thickness(value, target.Margin.Top, target.Margin.Right, target.Margin.Bottom);
}
public static void AddToLeftMargin(this FrameworkElement target, double valueToAdd)
{
SetLeftMargin(target, target.Margin.Left + valueToAdd);
}
}
public class VideoPlayerViewModel : ViewModelBase
{
private TimeSpan _movieElapsedTime = default(TimeSpan);
public TimeSpan MovieElapsedTime
{
get { return _movieElapsedTime; }
set
{
if (value != _movieElapsedTime)
{
_movieElapsedTime = value;
OnPropertyChanged();
}
}
}
private TimeSpan _movieLeftTime = default(TimeSpan);
public TimeSpan MovieLeftTime
{
get { return _movieLeftTime; }
set
{
if (value != _movieLeftTime)
{
_movieLeftTime = value;
OnPropertyChanged();
}
}
}
public void AlignTimersWithSource(TimeSpan currentPosition, MediaElement media)
{
MovieLeftTime = media.NaturalDuration.TimeSpan - currentPosition;
MovieElapsedTime = currentPosition;
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
I have tried to make the code copy/paste ready as requested in the comments, all of the Controls in the View's code behind are created in the XAML, if you want to fully replicate it.
The idea is to have a property and command in your VM for every area of the UI that you'd like to update or event that needs to be handled, respectively.
Just glancing at your current code, I think you will have a much easier time (you'll be able to remove a few of your event handlers) if you hook directly into your slider's Value property and bind it (two-way) to a property on your VM. Whenever the user drags, you will be able to see when the value updates and you can handle accordingly.
As far as the "hidden" effect of your scrub bar goes, you may have a much easier time just hooking into the visual state of your slider. Here are the styles and visual states.
EDIT:
public class VideoPlayerViewModel : ViewModelBase
{
// your existing properties here, if you decide that you still need them
// this could also be long/double, if you'd like to use it with your underlying type (DateTime.TotalTicks, TimeSpan.TotalSeconds, etc.)
private uint _elapsedTime = 0; //or default(uint), whichever you prefer
public uint ElapsedTime
{
get { return _elapsedTime; }
set
{
if (_elapsedTime != value)
{
_elapsedTime = value;
//additional "time changed" logic here, if needed
//if you want to skip programmatically, all you need to do is set this property!
OnPropertyChanged();
}
}
}
private double _maxTime = 0;
public double MaxTime
{
// you get the idea, you'll be binding to the media's end time in whatever unit you're using (i.e. if I have a 120 second clip, this value would be 120 and my elapsed time would be hooked into an underlying TimeSpan.TotalSeconds)
}
}
and on your slider:
Value={Binding ElapsedTime, Mode=TwoWay}
Maximum={Binding MaxTime, Mode=OneWay} //could also be OneTime, depending on the lifecycle of the control
I recommend using Caliburn Micro.
If you use that library you can bind events like this:
<Button cal:Message.Attach="Save">
or like that
<Button cal:Message.Attach="[Event MouseEnter] = [Action Save]">
Check out their website for more advanced possibilities:
https://caliburnmicro.codeplex.com/wikipage?title=Cheat%20Sheet
I have some simple rules that I follow in XAML apps:
The ViewModel should not know about the View, so no UI related code will ever be found in the ViewModel
All UI related code is in the code behind(xaml.cs)
User controls and dependency properties are your best friends, so use them. The view should be made up of user controls, each with its own ViewModel.
Inject your dependencies through constructor injection so they can be mocked when you write unit tests
You should not have mouse handlers in your viewmodel. Those events belong to the UI and hence the view. Instead, move the bloated view code to an attached behavior. From the behavior you can optionally call into your viewmodel through interfaces. E.g.:
var vm = AssociatedObject.DataContext as IPlayerViewModel;
vm?.AlignTimersWithSource(...);
you can not use events in viewmodel. So you will have to create command pattern class and just create viewmodel class. After that can use name space of viewmodel in xml file or view file using "xmlns tag. And create resource for the class and provide meaning full key name. And set datacontext in
<Grid datacontext="nameofresource">. Now do the keybinding.
Note: If you need more clearification, reply

how to call Resize Event in C#

I'm trying to create new Button with custom event. It's new for me. I'm trying to call "Resize". I wanna create "switch" like in android.
I'm trying to do this like other existing controls. I've been doing this for 2 days and i still have nothing. I belive that you will able to help me :)
Here is my code:
public abstract class SwitchBase : Control
{
private Button first;
private Button second;
public SwitchBase()
{
InitializeMySwitch();
}
private void InitializeMySwitch()
{
Controls.Add(first = new Button());
Controls.Add(second = new Button());
//first
first.Text = "first";
//second
second.Text = "second";
second.Location = new System.Drawing.Point(first.Location.X + first.Width, first.Location.Y);
}
public delegate void ChangedEventHandler(object source, EventArgs args);
public event ChangedEventHandler Changed;
protected virtual void OnSwitchChanged()
{
if (Changed != null)
Changed(this, EventArgs.Empty);
}
public delegate void ResizeEventHandler(object source, EventArgs args);
public event ResizeEventHandler Resize;
protected virtual void OnResize()
{
Resize(this, EventArgs.Empty);
}
}
public class Switch : SwitchBase
{
public Switch()
{
}
protected override void OnSwitchChanged()
{
base.OnSwitchChanged();
}
protected override void OnResize()
{
base.OnResize();
}
}
In another button I change the size of my switch
From reading your code, I gather that by "call Resize" you mean to raise the event. What you are doing is correct... although it should be noted that by the default event implementation, it will be null if there are no subscribers...
In fact, another thread could be unsubscribing behind your back. Because of that the advice is to take a copy.
You can do that as follows:
var resize = Resize;
if (resize != null)
{
resize(this, EventArgs.Empty)
}
It should be noted that the above code will call the subscribers to the event, but will not cause the cotrol to resize. If what you want is to change the size of your control, then do that:
this.Size = new Size(400, 200);
Or:
this.Width = 400;
this.Height = 200;
Note: I don't know what Control class you are using. In particular, if it were System.Windows.Forms.Control it already has a Resize event, and thus you won't be defining your own. Chances are you are using a Control class that doesn't even have Size or Width and Height.
Edit: System.Web.UI.Control doesn't have Resize, nor Size or Width and Height. But System.Windows.Controls.Control has Width and Height even thought it doesn't have Resize.

WinForms control's child form doesn't respond to mouse events when control is on modal dialog

I needed functionality that doesn't exist in the standard ComboBox, so I wrote my own from a TextBox and a form. When the user types in the TextBox, it shows a dropdown as a separate form.
Here's some of the relevant code:
internal class FilteredDropDown : Form
{
public Control OwnerControl { get; set; }
public bool CloseOnLostFocus { get; set; }
protected override OnLostFocus(EventArgs e)
{
if (CloseOnLostFocus && !OwnerControl.IsFocused)
this.Close();
}
protected override OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e)
// highlight the moused over item in the list
}
...
}
public class FilteredCombo : TextBox
{
private FilteredDropDown dropDown;
public FilteredCombo()
{
dropDown = new FilteredDropDown();
dropDown.OwnerControl = this;
}
public void ShowDropDown()
{
if (dropDown.Visible)
return;
dropDown.RefreshFilter();
var loc = PointToScreen(new Point(0, this.Height));
dropDown.Location = loc;
dropDown.CloseOnLostFocus = false;
int selectionStart = this.SelectionStart;
int selectionLength = this.SelectionLength;
dropDown.Show(this);
this.Focus();
this.SelectionStart = selectionStart;
this.SelectionLength = selectionLength;
dropDown.CloseOnLostFocus = false;
}
protected override OnLostFocus(EventArgs e)
{
if (dropDown.Visible && !dropDown.ContainsFocus())
dropDown.Close();
}
protected override OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
ShowDropDown();
}
...
}
There's obviously a whole lot more code than that to deal with all kinds of stuff irrelevent to my question.
The problem is when I put the FilteredCombo on a modal dialog. Somehow the FilteredDropDown form doesn't receive mouse events at all when it is parented by a modal dialog.
I've read something about WinForms filtering out events on all except the current modal dialog, I suspect that is what's going on, but I have no ideas of how to fix it. Is there some way to get the mouse up/down/move/click/etc. events to work when parented by a model dialog?
I had to go digging through the ShowDialog source code, and I found that it calls user32.dll EnableWindow(Handle, false) on all the windows except the shown one. The problem was that the FilteredDropDown already existed by the time the ShowDialog() method got called. I discovered two different ways to fix this:
Don't allow the DropDown to be shown until the parent form is shown. This is a bit trickier to guarantee, so I also implemented the second way.
Re-enable the DropDown window when it is made visible:
[DllImport("user32.dll")]
private static extern bool EnableWindow(IntPtr hWnd, bool enable);
protected override void OnVisibleChanged(EventArg e)
{
base.OnVisibleChanged(e);
if (this.Visible)
{
EnableWindow(this.Handle, true);
}
}

Update the MessageBoxText runtime using System.Windows.MessageBox?

I'm using System.Windows.MessageBox to show the message to user, But I want to update the text on that Messagebox, after showing it.
The example in my case, I want to show the MessageBox with the content that can change at runtime as below:
"The system will be restarted in 5 seconds"
"The system will be restarted in 4 seconds"
"The system will be restarted in 3 seconds"
"The system will be restarted in 2 seconds"
"The system will be restarted in 1 second"
"The system will be restarted in 0 second"
Someone can show me how to do it?
Many thanks,
T&T
I think it's easier to use another window instead of MessageBox. Then you turn off features that you don't want (resizing, close button), make it modal, set up timer event handling, and so on.
Someone can show me how to do it
You can't do it with standard messagebox i.e. System.Windows.MessageBox.
Alternative:
Though what you can do is to define a custom message box(a windows form) with a label on it that you update via event asynchronously. And you use that to display user the count down.
This can be possible with MessageBox from Extended WPF Toolkit.
It has Text dependency property, which can be data bound, but, unfortunately, MessageBox initialization is hidden, and solution contains more than single line:
First of all, we need our MessageBox ancestor, because we're going to call protected InitializeMessageBox method (to apply standard message box settings). Then, we need to make our Show overload, which will apply binding to Text:
class MyMessageBox : Xceed.Wpf.Toolkit.MessageBox
{
public static MessageBoxResult Show(object dataContext)
{
var messageBox = new MyMessageBox();
messageBox.InitializeMessageBox(null, null, "Hello", MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel);
messageBox.SetBinding(MyMessageBox.TextProperty, new Binding
{
Path = new PropertyPath("Text"),
Source = dataContext
});
messageBox.ShowDialog();
return messageBox.MessageBoxResult;
}
}
Next, we need a data context:
sealed class MyDataContext : INotifyPropertyChanged
{
public string Text
{
get { return text; }
set
{
if (text != value)
{
text = value;
OnPropertyChanged("Text");
}
}
}
private string text;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
And a code, which will change the text in message box:
public partial class MainWindow : Window
{
private readonly MyDataContext dataContext;
private readonly DispatcherTimer timer;
private int secondsElapsed;
public MainWindow()
{
InitializeComponent();
dataContext = new MyDataContext();
timer = new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.ApplicationIdle,
TimerElapsed, Dispatcher.CurrentDispatcher);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
secondsElapsed = 0;
timer.Start();
MyMessageBox.Show(dataContext);
timer.Stop();
}
private void TimerElapsed(object sender, EventArgs e)
{
dataContext.Text = string.Format("Elapsed {0} seconds.", secondsElapsed++);
}
}
The benefit of this approach is that you don't need to write yet another message box.

Mouse events on picturebox?

I have a "start" button with a custom image I have made. I haven't messed around with this part of C#.net, but I know a bit about VB.NET.
I've seen people have something like public void picturebox_MouseDown() and whatnot, but none seems to work. I am trying to change the image when a mouse event is given.
MouseDown would change the image to StartButtonDown
MouseUp would change the image to StartButtonUp
MouseEnter would change the image to StartButtonHover
MouseLeave would change the image to StartButtonUp
Is there something specific I should do, I've google'd this for about an hour and still haven't found anything to help me.
Here is something I wrote which is very similar to what you require.
using System;
using System.Drawing;
using System.Windows.Forms;
public partial class ImageButton : PictureBox
{
private Image _upImage, _downImage, _hoverImage;
[System.ComponentModel.Browsable(true),
System.ComponentModel.Category("Images")]
public Image UpImage
{
get { return _upImage; }
set
{
if (value != null)
{
_upImage = value;
this.Image = _upImage;
}
}
}
[System.ComponentModel.Browsable(true),
System.ComponentModel.Category("Images")]
public Image DownImage
{
get { return _downImage; }
set
{
if (value != null)
{
_downImage = value;
}
}
}
[System.ComponentModel.Browsable(true),
System.ComponentModel.Category("Images")]
public Image HoverImage
{
get { return _hoverImage; }
set
{
if (value != null)
{
_hoverImage = value;
}
}
}
public ImageButton()
{
InitializeComponent();
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (DownImage != null)
this.Image = DownImage;
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (UpImage != null)
this.Image = UpImage;
base.OnMouseUp(e);
}
protected override void OnMouseEnter(EventArgs e)
{
if (HoverImage != null)
this.Image = HoverImage;
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(EventArgs e)
{
if (UpImage != null)
this.Image = UpImage;
base.OnMouseLeave(e);
}
}
What I have done is inherrited from the standard PictureBoxto make an ImageButton. I have three properties for the Image to display with no mouse action (UpImage), the Image to display when the MouseDown event is triggered (DownImage), and the Image to display when the mouse is hovering over the control (HoverImage).
Note that you should add a check for the MouseUp and MouseLeave events. If I click on the image and drag the mouse away from the control, the control will go from the UpImage to the DownImage to the UpImage again because I have left the control (MouseLeave) even though my mouse is still down. You may desire that the DownImage remain displayed when the mouse leaves the control. Additionally, when the MouseUp event occurs, you should check if the mouse is still hovering over the control. If it is, you will want to display the HoverImage rather than the UpImage.
You could also check for which mouse button is used. Maybe you only want the images to change with left button clicks, not right or middle.
But this should get you started.

Categories