So I'm working on a C#/WPF Application and I'm relatively new to the language/environment. A couple weeks ago, I asked this question: How can I mimic this behavior in WPF?
I wanted to create a sort of pseudo modal popup that would appear/disappear with a click of a button. Adapting the answer given to me, I created the following two classes:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace RelayC
{
public partial class PopupBase : UserControl
{
public PopupBase()
{
this.Opacity = 0.0;
this.Visibility = Visibility.Hidden;
}
private void OpenPopup()
{
this.Opacity = 1.0;
this.Visibility = Visibility.Visible;
}
private void ClosePopup()
{
this.Opacity = 0.0;
this.Visibility = Visibility.Hidden;
}
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register(nameof(IsOpen),
typeof(bool),
typeof(PopupBase),
new PropertyMetadata(false,
new PropertyChangedCallback((s, e) =>
{
if (s is PopupBase popupBase && e.NewValue is bool boolean)
{
if (boolean)
{
popupBase.OpenPopup();
}
else
{
popupBase.ClosePopup();
}
}
})));
}
public class PopupAttach
{
public static PopupBase GetPopup(ButtonBase button)
=> (PopupBase)button.GetValue(PopupProperty);
public static void SetPopup(ButtonBase button, PopupBase value)
=> button.SetValue(PopupProperty, value);
public static readonly DependencyProperty PopupProperty =
DependencyProperty.RegisterAttached("Popup",
typeof(PopupBase),
typeof(PopupAttach),
new PropertyMetadata(null,
new PropertyChangedCallback((s, e) =>
{
if (s is ButtonBase button && e.NewValue is PopupBase newPopup)
{
if (Application.Current.MainWindow.Content is Grid grid)
{
if (e.OldValue is PopupBase oldPopup)
{
grid.Children.Remove(oldPopup);
}
grid.Children.Add(newPopup);
button.Click -= buttonClick;
button.Click += buttonClick;
}
else
{
throw new Exception($"{nameof(Application.Current.MainWindow)} must have a root layout panel of type {nameof(Grid)} in order to use attachable Flyout.");
}
void buttonClick(object sender, RoutedEventArgs routedEventArgs)
{
newPopup.IsOpen = true;
}
}
})));
}
}
And then I have any UserControl I create inherit from PopupBase so my Popup UI's look sort of like this:
<local:PopupBase x:Class="RelayC.AddServer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RelayC"
mc:Ignorable="d"
Width="500"
Height="375">
UI CONTENT INSERTED HERE
</local:PopupBase>
The problem is when I want to attach it to a button, I have to do something like this:
<Window.Resources>
<local:AddServer Grid.Row="1" x:Key="ISERCpm"/>
</Window.Resources>
in my MainWindow.xaml and then call it from a button using
<Button local:PopupAttach.Popup="{StaticResource ISERCpm}" />
Is there anyway to skip the Window Resource declaration and straight just call my PopupBase User Control?
PopupAttach.Popup is an attached dependency property. It is possible to use property element syntax instead of attribute syntax:
<Button>
<local:PopupAttach.Popup>
<local:AddServer Grid.Row="1"/>
</local:PopupAttach.Popup>
</Button>
Related
How could I access a XAML object in my ViewModel? I am really confused. I want to access the <Controls:ModalContentPresenter> object. How could I realise this in a MVVM conform way? On this object I want to call a method ShowModalContent
<Controls:ModalContentPresenter x:Name="modalContent">
<ScrollViewer Behaviors:AdvancedZooming.KeepInCenter="true" Visibility="{Binding LeerformularIsVisible}" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Viewbox Stretch="Uniform">
<Grid>
<DataGrid BorderBrush="{x:Null}">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding AddFieldDefinitionCommand}" Header="Feld hinterlegen" Icon="pack://application:,,,/Images/Designer/field.png" />
<MenuItem Command="{Binding AddFunctionCommand}" Header="Funktion hinterlegen" Icon="pack://application:,,,/Images/Designer/FI_Taschenmesser_16x16.png" />
<MenuItem Command="{Binding RemoveFieldDefinitionCommand}" Header="Aktuelle Felddefinition entfernen" Icon="pack://application:,,,/Images/Designer/remove_field.png" />
<MenuItem Command="{Binding CutCommand}" Header="Ausschneiden" Icon="pack://application:,,,/Images/Zwischenablage/FI_Ausschneiden_16x16.png" />
<MenuItem Command="{Binding CopyCommand}" Header="Kopieren" Icon="pack://application:,,,/Images/Zwischenablage/FI_Kopieren_16x16.png" />
<MenuItem Command="{Binding PasteCommand}" Header="Einfügen" Icon="pack://application:,,,/Images/Zwischenablage/FI_Einfuegen_16x16.png" />
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
</Viewbox>
</ScrollViewer>
<Controls:ModalContentPresenter.ModalContent>
<StackPanel>
<TextBlock>Test</TextBlock>
<Button>Hide</Button>
</StackPanel>
</Controls:ModalContentPresenter.ModalContent>
</Controls:ModalContentPresenter>
The code of ModalContentPresenter can be found here:
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
namespace Controls
{
[ContentProperty("Content")]
public class ModalContentPresenter : FrameworkElement
{
#region private fields
private Panel layoutRoot;
private ContentPresenter primaryContentPresenter;
private ContentPresenter modalContentPresenter;
private Border overlay;
private object[] logicalChildren;
private KeyboardNavigationMode cachedKeyboardNavigationMode;
private static readonly TraversalRequest traversalDirection;
#endregion
#region dependency properties
public static readonly DependencyProperty IsModalProperty = DependencyProperty.Register("IsModal", typeof(bool), typeof(ModalContentPresenter),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsModalChanged));
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(ModalContentPresenter),
new UIPropertyMetadata(null, OnContentChanged));
public static readonly DependencyProperty ModalContentProperty = DependencyProperty.Register("ModalContent", typeof(object), typeof(ModalContentPresenter),
new UIPropertyMetadata(null, OnModalContentChanged));
public static readonly DependencyProperty OverlayBrushProperty = DependencyProperty.Register("OverlayBrush", typeof(Brush), typeof(ModalContentPresenter),
new UIPropertyMetadata(new SolidColorBrush(Color.FromArgb(204, 169, 169, 169)), OnOverlayBrushChanged));
public bool IsModal
{
get { return (bool)GetValue(IsModalProperty); }
set { SetValue(IsModalProperty, value); }
}
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public object ModalContent
{
get { return (object)GetValue(ModalContentProperty); }
set { SetValue(ModalContentProperty, value); }
}
public Brush OverlayBrush
{
get { return (Brush)GetValue(OverlayBrushProperty); }
set { SetValue(OverlayBrushProperty, value); }
}
#endregion
#region routed events
public static readonly RoutedEvent PreviewModalContentShownEvent = EventManager.RegisterRoutedEvent("PreviewModalContentShown", RoutingStrategy.Tunnel,
typeof(RoutedEventArgs), typeof(ModalContentPresenter));
public static readonly RoutedEvent ModalContentShownEvent = EventManager.RegisterRoutedEvent("ModalContentShown", RoutingStrategy.Bubble,
typeof(RoutedEventArgs), typeof(ModalContentPresenter));
public static readonly RoutedEvent PreviewModalContentHiddenEvent = EventManager.RegisterRoutedEvent("PreviewModalContentHidden", RoutingStrategy.Tunnel,
typeof(RoutedEventArgs), typeof(ModalContentPresenter));
public static readonly RoutedEvent ModalContentHiddenEvent = EventManager.RegisterRoutedEvent("ModalContentHidden", RoutingStrategy.Bubble,
typeof(RoutedEventArgs), typeof(ModalContentPresenter));
public event RoutedEventHandler PreviewModalContentShown
{
add { AddHandler(PreviewModalContentShownEvent, value); }
remove { RemoveHandler(PreviewModalContentShownEvent, value); }
}
public event RoutedEventHandler ModalContentShown
{
add { AddHandler(ModalContentShownEvent, value); }
remove { RemoveHandler(ModalContentShownEvent, value); }
}
public event RoutedEventHandler PreviewModalContentHidden
{
add { AddHandler(PreviewModalContentHiddenEvent, value); }
remove { RemoveHandler(PreviewModalContentHiddenEvent, value); }
}
public event RoutedEventHandler ModalContentHidden
{
add { AddHandler(ModalContentHiddenEvent, value); }
remove { RemoveHandler(ModalContentHiddenEvent, value); }
}
#endregion
#region ModalContentPresenter implementation
static ModalContentPresenter()
{
traversalDirection = new TraversalRequest(FocusNavigationDirection.First);
}
public ModalContentPresenter()
{
layoutRoot = new ModalContentPresenterPanel();
primaryContentPresenter = new ContentPresenter();
modalContentPresenter = new ContentPresenter();
overlay = new Border();
AddVisualChild(layoutRoot);
logicalChildren = new object[2];
overlay.Background = OverlayBrush;
overlay.Child = modalContentPresenter;
overlay.Visibility = Visibility.Hidden;
layoutRoot.Children.Add(primaryContentPresenter);
layoutRoot.Children.Add(overlay);
}
public void ShowModalContent()
{
if (!IsModal)
IsModal = true;
}
public void HideModalContent()
{
if (IsModal)
IsModal = false;
}
private void RaiseModalContentShownEvents()
{
RoutedEventArgs args = new RoutedEventArgs(PreviewModalContentShownEvent);
OnPreviewModalContentShown(args);
if (!args.Handled)
{
args = new RoutedEventArgs(ModalContentShownEvent);
OnModalContentShown(args);
}
}
private void RaiseModalContentHiddenEvents()
{
RoutedEventArgs args = new RoutedEventArgs(PreviewModalContentHiddenEvent);
OnPreviewModalContentHidden(args);
if (!args.Handled)
{
args = new RoutedEventArgs(ModalContentHiddenEvent);
OnModalContentHidden(args);
}
}
protected virtual void OnPreviewModalContentShown(RoutedEventArgs e)
{
RaiseEvent(e);
}
protected virtual void OnModalContentShown(RoutedEventArgs e)
{
RaiseEvent(e);
}
protected virtual void OnPreviewModalContentHidden(RoutedEventArgs e)
{
RaiseEvent(e);
}
protected virtual void OnModalContentHidden(RoutedEventArgs e)
{
RaiseEvent(e);
}
#endregion
#region property changed callbacks
private static void OnIsModalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
if ((bool)e.NewValue == true)
{
control.cachedKeyboardNavigationMode = KeyboardNavigation.GetTabNavigation(control.primaryContentPresenter);
KeyboardNavigation.SetTabNavigation(control.primaryContentPresenter, KeyboardNavigationMode.None);
control.overlay.Visibility = Visibility.Visible;
control.overlay.MoveFocus(traversalDirection);
control.RaiseModalContentShownEvents();
}
else
{
control.overlay.Visibility = Visibility.Hidden;
KeyboardNavigation.SetTabNavigation(control.primaryContentPresenter, control.cachedKeyboardNavigationMode);
control.primaryContentPresenter.MoveFocus(traversalDirection);
control.RaiseModalContentHiddenEvents();
}
}
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
if (e.OldValue != null)
control.RemoveLogicalChild(e.OldValue);
control.primaryContentPresenter.Content = e.NewValue;
control.AddLogicalChild(e.NewValue);
control.logicalChildren[0] = e.NewValue;
}
private static void OnModalContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
if (e.OldValue != null)
control.RemoveLogicalChild(e.OldValue);
control.modalContentPresenter.Content = e.NewValue;
control.AddLogicalChild(e.NewValue);
control.logicalChildren[1] = e.NewValue;
}
private static void OnOverlayBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
control.overlay.Background = (Brush)e.NewValue;
}
#endregion
#region FrameworkElement overrides
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index > 1)
throw new ArgumentOutOfRangeException("index");
return layoutRoot;
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override IEnumerator LogicalChildren
{
get { return logicalChildren.GetEnumerator(); }
}
protected override Size ArrangeOverride(Size finalSize)
{
layoutRoot.Arrange(new Rect(finalSize));
return finalSize;
}
protected override Size MeasureOverride(Size availableSize)
{
layoutRoot.Measure(availableSize);
return layoutRoot.DesiredSize;
}
#endregion
#region layout panel
class ModalContentPresenterPanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
Size resultSize = new Size(0, 0);
foreach (UIElement child in Children)
{
child.Measure(availableSize);
resultSize.Width = Math.Max(resultSize.Width, child.DesiredSize.Width);
resultSize.Height = Math.Max(resultSize.Height, child.DesiredSize.Height);
}
return resultSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in InternalChildren)
{
child.Arrange(new Rect(finalSize));
}
return finalSize;
}
}
#endregion
}
}
You shouldn't do this (at least, this is definitely not a MVVM-way).
Use IsModal property and data binding instead:
<Controls:ModalContentPresenter x:Name="modalContent" IsModal="{Binding IsModal}">
where IsModal in the binding expression is a bound data property within your view model.
In MVVM, any data going back and forth should be done via data binding properties on your View Model. That includes virtually any properties of the ContentPresenter - just add a binding. However, if you want call a method of a XAML object in an MVVM-friendly way, you can use an Action.
Let's suppose I wanted to set the focus on a textbox via the view model (contrived example, I know there's other MVVM-ways to do this- just wanted an example that requires a child object of the view).
First, give the textbox that should get the focus a name in your XAML, i.e.,
<TextBox x:Name="textBox1"/>
On your view model, add a property for the Action:
public Action FocusAction {get;set;}
Some time before or as your view is loading, get your DataContext (i.e., your view model) and add the Action to the code behind (the view's .cs file):
ViewModel vm = (ViewModel)this.DataContext;
if ( vm.FocusAction == null )
vm.FocusAction= new Action(() => this.textBox1.Focus());
For instance, you might implement the IsLoaded event and do it there. You could also do this in your constructor after InitializeComponent, as long as it either created your view model instance or it was passed in as a paramater. The key is that the view model is already instantiated and assigned to the view's data context.
Then in your view model, wherever you wanted to add focus to that textbox, call:
FocusAction();
Since what I just showed above requires that your view cast the DataContext to a particular view model, what I do is create an interface for the kinds of actions I need, like this:
interface IFocusable
{
Action FocusAction {get;set;}
}
Then I make my view model implement that inteface. With that done, in my view's code-behind, I can say something like:
if(this.DataContext is IFocusable)
((IFocusable)this.DataContext).FocusAction = new Action(() => this.textBox1.Focus());
I think that makes it more MVVM-compliant, since it's not tightly-coupled to a particular view model, the view just knows it can add an action if the view model is the type of view model that can use it.
More details and another example available here: http://jkshay.com/closing-a-wpf-window-using-mvvm-and-minimal-
code-behind/
I know it's been few years but I just faced the same, so here's my answer in case someone will find it useful...
In your xaml file refer to your ViewModel class in the DataContext:
<Window.DataContext>
<local:YourViewModel x:Name="yourViewModel"/>
</Window.DataContext>
In your ViewModel class create a public function with your xaml file as argument and a private member to hold it:
private MainWindow mainWindow;
public void OnViewInitialized(MainWindow mainWindow)
{
this.mainWindow = mainWindow;
}
In your code behind use the function pass the the 'yourViewModel' you defined in the xaml:
public MainWindow()
{
InitializeComponent();
yourViewModel.OnViewInitialized(this);
}
And that's it :)
Now you can access all your xaml elements from your ViewModel by using your mainWindow member, just give them a name.
mainWindow.textBox1
you can create a constructor for view model which accepts the ContentPage as its parameter.
public class ViewModelClass
{
public ViewModelClass(ContentPage p=null)
{...}
}
then set the binding context in Contentpage back code script passing the referenc of contentpage to viewmodel
public class ContentPageClass : ContentPage
{
public ContentPageClass()
{
BindingContext = new ViewModelClass(p:this);
}
}
I have several WPF TextBoxes in my application that will be used for specifying file names. I'm looking for a solution that will quickly and easily allow me to disallow special characters (i.e. "\ / : ? " < > |") from being entered into these textboxes, without having to create a custom control that inherits from TextBox and doesn't require the use of Regular Expressions.
I created a static class called "DisallowSpecialCharatersTextBoxBehavior" that that harnesses the power of Attachable Properties in WPF, like so:
public static class DisallowSpecialCharactersTextboxBehavior
{
public static DependencyProperty DisallowSpecialCharactersProperty =
DependencyProperty.RegisterAttached("DisallowSpecialCharacters", typeof(bool), typeof(DisallowSpecialCharactersTextboxBehavior), new PropertyMetadata(DisallowSpecialCharactersChanged));
public static void SetDisallowSpecialCharacters(TextBox textBox, bool disallow)
{
textBox.SetValue(DisallowSpecialCharactersProperty, disallow);
}
public static bool GetDisallowSpecialCharacters(TextBox textBox)
{
return (bool)textBox.GetValue(DisallowSpecialCharactersProperty);
}
private static void DisallowSpecialCharactersChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var tb = dependencyObject as TextBox;
if (tb != null)
{
if ((bool)e.NewValue)
{
tb.PreviewTextInput += tb_PreviewTextInput;
tb.AddHandler(DataObject.PastingEvent, new DataObjectPastingEventHandler(tb_Pasting));
}
else
{
tb.PreviewTextInput -= tb_PreviewTextInput;
tb.RemoveHandler(DataObject.PastingEvent, new DataObjectPastingEventHandler(tb_Pasting));
}
}
}
private static void tb_Pasting(object sender, DataObjectPastingEventArgs e)
{
var pastedText = e.DataObject.GetData(typeof(string)) as string;
Path.GetInvalidFileNameChars().ToList().ForEach(c =>
{
if (pastedText.Contains(c))
{
e.CancelCommand();
}
});
}
private static void tb_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
if (Path.GetInvalidFileNameChars().ToList().ConvertAll(x => x.ToString()).Contains(e.Text))
{
e.Handled = true;
}
}
}
It can easily be applied to any TextBox in WPF like so:
<Window x:Class="ExampleApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ExampleApp"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox local:DisallowSpecialCharactersTextboxBehavior.DisallowSpecialCharacters="true" />
</Grid>
</Window>
I'm trying to pass an object created in MainWindow to my UserControl that will read and modify it but it doesn't don't know why. Here is the code I'm using:
MainWindow class:
public partial class MainWindow : Window
{
public SupremeLibrary.Player player = new SupremeLibrary.Player();
public MainWindow()
{
InitializeComponent();
MusicSeekBar = new Components.SeekBar(player);
}
}
And SeekBar user control:
public partial class SeekBar : UserControl
{
DispatcherTimer Updater = new DispatcherTimer();
SupremeLibrary.Player player;
/// <summary>
/// Initialize new Seekbar
/// </summary>
public SeekBar()
{
InitializeComponent();
InitializeUpdater();
}
public SeekBar(SupremeLibrary.Player _player)
{
player = _player;
InitializeComponent();
InitializeUpdater();
}
private void InitializeUpdater()
{
Updater.Interval = TimeSpan.FromMilliseconds(100);
Updater.Tick += UpdateSeekBar;
Updater.Start();
}
private void UpdateSeekBar(object sender, EventArgs e)
{
if (player != null)
{
if (player.PlaybackState == SupremeLibrary.PlaybackStates.Playing)
{
if (player.Position.TotalMilliseconds != CustomProgressBar.Value) CustomProgressBar.Value = player.Position.TotalMilliseconds;
if (player.MaxPosition.TotalMilliseconds != CustomProgressBar.Maximum) CustomProgressBar.Maximum = player.MaxPosition.TotalMilliseconds;
}
}
}
private void PB_SeekBar_ChangeValue(object obj, MouseEventArgs e)
{
if (player != null)
{
if (player.PlaybackState == SupremeLibrary.PlaybackStates.Playing)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
player.Position = TimeSpan.FromMilliseconds(e.GetPosition(obj as ProgressBar).X / ((obj as ProgressBar).ActualWidth / 100) * ((obj as ProgressBar).Maximum / 100));
}
}
}
}
In add, it works if I use
public SupremeLibrary.Player player = new SupremeLibrary.Player();
as static and call it in UserControl as MainWindow.player but it's ugly and I don't want to use it.
I have tried to pass player from MainWindow as reference but it doesn't seem to work either.
Example using MediaElement
user control SeekBar
XAML
<UserControl x:Class="CSharpWPF.SeekBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}" >
<Slider Maximum="{Binding TotalMilliseconds}"
Value="{Binding CurrentPosition}"/>
</UserControl>
i have defined a Slider in the control with binding to the maximum and value property to the TotalMilliseconds and CurrentPosition of the control, the properties will be bound to the control itself as I have set the DataContext of the control to self
.cs
public partial class SeekBar : UserControl
{
DispatcherTimer Updater = new DispatcherTimer();
/// <summary>
/// Initialize new Seekbar
/// </summary>
public SeekBar()
{
InitializeComponent();
InitializeUpdater();
}
private void InitializeUpdater()
{
Updater.Interval = TimeSpan.FromMilliseconds(100);
Updater.Tick += UpdateSeekBar;
}
public MediaElement Player
{
get { return (MediaElement)GetValue(PlayerProperty); }
set { SetValue(PlayerProperty, value); }
}
// Using a DependencyProperty as the backing store for Player. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PlayerProperty =
DependencyProperty.Register("Player", typeof(MediaElement), typeof(SeekBar), new PropertyMetadata(null, OnPlayerChanged));
private static void OnPlayerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SeekBar seekB = d as SeekBar;
if (e.OldValue != null)
{
SeekBar oldSeekB = (e.OldValue as SeekBar);
oldSeekB.Player.MediaOpened -= seekB.Player_MediaOpened;
oldSeekB.Player.MediaEnded -= seekB.Player_MediaEnded;
}
if (seekB.Player != null)
{
seekB.Player.MediaOpened += seekB.Player_MediaOpened;
seekB.Player.MediaEnded += seekB.Player_MediaEnded;
}
}
void Player_MediaEnded(object sender, RoutedEventArgs e)
{
Updater.Stop();
}
private void Player_MediaOpened(object sender, RoutedEventArgs e)
{
if (Player.NaturalDuration.HasTimeSpan)
{
TotalMilliseconds = Player.NaturalDuration.TimeSpan.TotalMilliseconds;
Updater.Start();
}
else
{
CurrentPosition = 0.0;
TotalMilliseconds = 1.0;
}
}
public double CurrentPosition
{
get { return (double)GetValue(CurrentPositionProperty); }
set { SetValue(CurrentPositionProperty, value); }
}
// Using a DependencyProperty as the backing store for CurrentPosition. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentPositionProperty =
DependencyProperty.Register("CurrentPosition", typeof(double), typeof(SeekBar), new PropertyMetadata(1.0, OnCurrentPositionChange));
private static void OnCurrentPositionChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SeekBar seekB = d as SeekBar;
if (seekB.Player != null)
{
seekB.Player.Position = TimeSpan.FromMilliseconds(seekB.CurrentPosition);
}
}
public double TotalMilliseconds
{
get { return (double)GetValue(TotalMillisecondsProperty); }
set { SetValue(TotalMillisecondsProperty, value); }
}
// Using a DependencyProperty as the backing store for TotalMilliseconds. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TotalMillisecondsProperty =
DependencyProperty.Register("TotalMilliseconds", typeof(double), typeof(SeekBar), new PropertyMetadata(0.0));
private void UpdateSeekBar(object sender, EventArgs e)
{
if (Player != null && TotalMilliseconds > 1)
{
CurrentPosition = Player.Position.TotalMilliseconds;
}
}
}
what I have done
defined property Player of Media Element to be bound at UI
attached MediaOpened and MediaEnded for the purpose of starting and stopping the timer adn updating the duration.
defined properties for current position and total duration for my slider control in the control UI
and on change of CurrentPosition, I'll update back the player's position.
usage in main window
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<MediaElement x:Name="media"
Source="Wildlife.wmv" />
<l:SeekBar Grid.Row="1"
Player="{Binding ElementName=media}" />
</Grid>
I'll just bind the media element to the Player property of my SeekBar control
by doing in this way I've not done any hard coding in code behind, also by means of interface you can achieve a greater decoupling between your seekbar and the player
this is just a simple example for your case, you may use your custom player and progress control in the above example to achieve your results.
I have a windows 8 XAML/C# application using the MVVM pattern.
All my textboxes on the form have their text properties bound to properties on my MVVM class.
So, one of my textboxes looks like this:
<TextBox x:Name="textAddressLine1" Text="{Binding AddressLine1, Mode=TwoWay}"/>
And that property on the MVVM class looks like this:
private string addressLine1;
public string AddressLine1
{
get { return addressLine1; }
set
{
if (addressLine1 == value)
{
return;
}
addressLine1 = value;
RaisePropertyChanged("AddressLine1");
}
}
As I type into my textbox the MVVM class isn't updated. It only gets updated once the focus moves to a different control.
How can I update the MVVM class property when the text changes on my textbox?
Thanks in advance
Use explicit binding for
textAddressLine1
<TextBox x:Name="textAddressLine1" Text="{Binding AddressLine1,UpdateSourceTrigger=Explicit, Mode=TwoWay}" TextChanged="textAddressLine1_Changed"/>
private void textAddressLine1_Changed(object sender, RoutedEventArgs e)
{
BindingExpression be = textAddressLine1.GetBindingExpression(TextBox.TextProperty);
be.UpdateTarget();
}
I didn't test the code but should work.
EDIT: I see it UpdateSourceTrigger is not exist for environtment
You can create a your viewmodel as instance and give it as datacontext by the way you can easily perform your viewmodel from your code-behind. For this type cases it saves the day!
public MyClassViewModel ViewModel {get;set}
ctor()
{
this.ViewModel=new MyClassViewModel();
this.DataContext=this.ViewModel;
InitializeComponets();
}
private void textAddressLine1_Changed(object sender, RoutedEventArgs e)
{
this.ViewModel.AddressLine1=textAddressLine1.Text;
}
I think you will not need two way by this way. OneWay is ok. Because you explicitly change VM.
Note:I coded on SO, didn't test again.Hope helps!
I had the same issue, I found here: https://stackoverflow.com/a/11676076/4551080
<TextBox Text="{Binding Path=EmailAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
So make sure to set UpdateSourceTrigger=PropertyChanged Default Value is LostFocus
Use this workaround:
public class ExtendedTextBox : TextBox
{
public static readonly DependencyProperty CustomActionProperty =
DependencyProperty.Register(
"CustomAction",
typeof(Action<string>),
typeof(ExtendedTextBox),
new PropertyMetadata(null, OnPropertyChanged));
public Action<string> CustomAction
{
get
{
return (Action<string>)GetValue(CustomActionProperty);
}
set
{
SetValue(CustomActionProperty, value);
}
}
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue != null)
(d as ExtendedTextBox).TextChanged += ExtendedTextBox_TextChanged;
else
(d as ExtendedTextBox).TextChanged -= ExtendedTextBox_TextChanged;
}
async static void ExtendedTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => (sender as ExtendedTextBox).CustomAction((sender as ExtendedTextBox).Text));
}
}
IN your model:
public Action<string> UpdateBindedViewModelProperty
{
get { return new Action<string>((value) => NewLabelName = value); }
}
and view:
<plmrfc:extendedtextbox customaction="{Binding UpdateBindedViewModelProperty, Mode=OneTime}" text="{Binding Path=NewLabelName, Mode=TwoWay}" width="200" x:name="Label_TextBox"></plmrfc:extendedtextbox>
There is also another way, which does not involve subclassing TextBox. Maybe you like this one more:
using System.Reflection;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace Flexman
{
public class TextBoxUpdateSourceBehaviour
{
private static PropertyInfo _boundProperty;
public static readonly DependencyProperty BindingSourceProperty =
DependencyProperty.RegisterAttached(
"BindingSource",
typeof(string),
typeof(TextBoxUpdateSourceBehaviour),
new PropertyMetadata(default(string), OnBindingChanged));
public static void SetBindingSource(TextBox element, string value)
{
element.SetValue(BindingSourceProperty, value);
}
public static string GetBindingSource(TextBox element)
{
return (string)element.GetValue(BindingSourceProperty);
}
private static void OnBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var txt = d as TextBox;
if (txt == null)
return;
txt.Loaded += OnLoaded;
txt.TextChanged += OnTextChanged;
}
static void OnLoaded(object sender, RoutedEventArgs e)
{
var txt = sender as TextBox;
if (txt == null)
return;
// Reflect the datacontext of the textbox to find the field to bind to.
var dataContextType = txt.DataContext.GetType();
_boundProperty = dataContextType.GetRuntimeProperty(GetBindingSource(txt));
// If you want the behaviour to handle your binding as well, uncomment the following.
//var binding = new Binding();
//binding.Mode = BindingMode.TwoWay;
//binding.Path = new PropertyPath(GetBindingSource(txt));
//binding.Source = txt.DataContext;
//BindingOperations.SetBinding(txt, TextBox.TextProperty, binding);
}
static void OnTextChanged(object sender, TextChangedEventArgs e)
{
var txt = sender as TextBox;
if (txt == null)
return;
if (_boundProperty.GetValue(txt.DataContext).Equals(txt.Text)) return;
_boundProperty.SetValue(txt.DataContext, txt.Text);
}
}
}
and view
<TextBox Text="{Binding Username}" Flexman:TextBoxUpdateSourceBehaviour.BindingSource="Username" />
This is the prettiest solution I know of. Others will be "non-generic" hacks.
Good luck. I do agree that it's major downgrade from silverlight/WPF but hey, there are a lot of more horrible things in WinRT that are missing in WPF :)
Is there a MVVM way to select text in a textbox? The MVVM framework that I am using is Laurent Bugnion's MVVM Light Toolkit.
Whenever I am trying to directly affect the the View in a "pure" MVVM application (no code-behind in View), I will use Attached Properties to encapsulate whatever effect I am trying to achieve. I will create an interface that defines the actions I wish to take using custom events. I then implement this interface in each ViewModel that will be "running" these commands on the View. Finally, I bind my ViewModel to the attached property in my View definition. The following code shows how to this for SelectAll and a TextBox. This code can be easily expanded to perform just about any action on any component in the View.
My Attached Property and interface definition:
using System.Windows;
using System.Windows.Controls;
using System;
using System.Collections.Generic;
namespace SelectAllSample
{
public static class TextBoxAttach
{
public static readonly DependencyProperty TextBoxControllerProperty = DependencyProperty.RegisterAttached(
"TextBoxController", typeof(ITextBoxController), typeof(TextBoxAttach),
new FrameworkPropertyMetadata(null, OnTextBoxControllerChanged));
public static void SetTextBoxController(UIElement element, ITextBoxController value)
{
element.SetValue(TextBoxControllerProperty, value);
}
public static ITextBoxController GetTextBoxController(UIElement element)
{
return (ITextBoxController)element.GetValue(TextBoxControllerProperty);
}
private static readonly Dictionary<ITextBoxController, TextBox> elements = new Dictionary<ITextBoxController, TextBox>();
private static void OnTextBoxControllerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = d as TextBox;
if (element == null)
throw new ArgumentNullException("d");
var oldController = e.OldValue as ITextBoxController;
if (oldController != null)
{
elements.Remove(oldController);
oldController.SelectAll -= SelectAll;
}
var newController = e.NewValue as ITextBoxController;
if (newController != null)
{
elements.Add(newController, element);
newController.SelectAll += SelectAll;
}
}
private static void SelectAll(ITextBoxController sender)
{
TextBox element;
if (!elements.TryGetValue(sender, out element))
throw new ArgumentException("sender");
element.Focus();
element.SelectAll();
}
}
public interface ITextBoxController
{
event SelectAllEventHandler SelectAll;
}
public delegate void SelectAllEventHandler(ITextBoxController sender);
}
My ViewModel definition:
public class MyViewModel : ITextBoxController
{
public MyViewModel()
{
Value = "My Text";
SelectAllCommand = new RelayCommand(p =>
{
if (SelectAll != null)
SelectAll(this);
});
}
public string Value { get; set; }
public RelayCommand SelectAllCommand { get; private set; }
public event SelectAllEventHandler SelectAll;
}
My View definition:
<Window x:Class="SelectAllSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:SelectAllSample"
Title="Window1" Height="150" Width="150">
<x:Code><![CDATA[
public Window1()
{
InitializeComponent();
DataContext = new MyViewModel();
}
]]></x:Code>
<StackPanel>
<TextBox Text="{Binding Value}" loc:TextBoxAttach.TextBoxController="{Binding}" />
<Button Content="Select All" Command="{Binding SelectAllCommand}" />
</StackPanel>
</Window>
Note: Thanks to Josh Smith for RelayCommand (see code in Figure 3 on this page). It is used in MyViewModel in this example (and just about all my MVVM code).
find a good introduction to attached properties here:
http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx