Popup in style of ContentControl not updating - c#

I have a ContentControl with a style that contains a popup that wraps a textbox. I know it sounds a little confusing but I will post some code below. When the caps lock key is on the popup is shown, but when the window is dragged the popup does not move with it.
I need to figure out how to update the location of the popup in they style.
This ContentControl is used on both a window and UserControl so that is why I am trying to address this in the style.
This question differs from some of the others as I am trying to solve it in the style and not code.
The content control:
public class ShowCapLockWarningControler : ContentControl
{
static ShowCapLockWarningControler()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ShowCapLockWarningControler), new FrameworkPropertyMetadata(typeof(ShowCapLockWarningControler)));
}
public static readonly DependencyProperty ShowMessageProperty =
DependencyProperty.Register(Reflection.GetPropertyName<ShowCapLockWarningControler>(i => i.ShowMessage), typeof(bool),
typeof(ShowCapLockWarningControler), new PropertyMetadata(false));
public bool ShowMessage
{
get { return (bool)GetValue(ShowMessageProperty); }
set { SetValue(ShowMessageProperty, value); }
}
public ShowCapLockWarningControler()
{
IsKeyboardFocusWithinChanged += (s, e) => RecomputeShowMessage();
PreviewKeyDown += (s, e) => RecomputeShowMessage();
PreviewKeyUp += (s, e) => RecomputeShowMessage();
}
private void RecomputeShowMessage()
{
ShowMessage = IsKeyboardFocusWithin && Console.CapsLock;
}
}
How its used:
<controls:ShowCapLockWarningControler Grid.Row="1" Grid.Column="2" Style="{DynamicResource CaplockWarning}">
<PasswordBox Width="150" Name="PasswordBox" PasswordChanged="HandlePasswordChanged" VerticalContentAlignment="Center"
KeyDown="HandlePasswordBoxEnterPressed"/>
</controls:ShowCapLockWarningControler>
The style in the style dictionary:
<Style x:Key="CaplockWarning" TargetType="{x:Type controls:ShowCapLockWarningControler}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:ShowCapLockWarningControler}">
<Grid>
<ContentPresenter Name="Presenter"/>
<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=Presenter}" Name="BalloonPopup" AllowsTransparency="True"
IsOpen="{TemplateBinding ShowMessage}" >
<!-- Visual of the popup-->
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Use Window.LocationChanged event, Popup.Placement = AbsolutePoint, Popup.HorizontalOffset, Popup.VerticalOffset .
In the sample below, Popup appears when ContentControl's Loaded event is fired. And we change relevant Popup properties when Window changes its location.
Code :
<ContentControl x:Name="CntCtrl" Height="35" Content="Some content" Loaded="CntCtrl_Loaded_1"/>
<Popup PlacementTarget="{Binding ElementName=CntCtrl}" Placement="AbsolutePoint" x:Name="Popup1">
<ListBox>
<ListBoxItem>item1</ListBoxItem>
<ListBoxItem>item1</ListBoxItem>
<ListBoxItem>item1</ListBoxItem>
<ListBoxItem>item1</ListBoxItem>
<ListBoxItem>item1</ListBoxItem>
<ListBoxItem>item1</ListBoxItem>
<ListBoxItem>item1</ListBoxItem>
</ListBox>
</Popup>
Code :
private void Window_LocationChanged_1(object sender, EventArgs e)
{
Point ptb = CntCtrl.PointToScreen(new Point(0, 0));
Popup1.HorizontalOffset = ptb.X;
Popup1.VerticalOffset = ptb.Y + CntCtrl.Height;
}
private void CntCtrl_Loaded_1(object sender, RoutedEventArgs e)
{
Popup1.IsOpen = true;
Point ptb = CntCtrl.PointToScreen(new Point(0, 0));
Popup1.HorizontalOffset = ptb.X;
Popup1.VerticalOffset = ptb.Y + CntCtrl.Height;
}

Related

WPF DataGrid seems to ignore IsTabStop setting, when pressing Enter

I have a datagrid with quite a few columns. Now normally user can press TAB to cycle between controls in the DataGrid cells. Data grid cells have been set to IsTabStop = false, so it's skipping focusing on the the cell itself and it just falls through until focus is given to an actual control.
This seems to NOT WORK if the user presses Enter because they want to work within that same column. DataGrid will pass the focus to the next cell in the COLUMN, BUT it will also ignore the IsTabStop setting, so it just gives focus to the cell itself not the control inside.
Any ideas how to fix this.
Short example
<DataGrid AutoGenerateColumns="False" Name="grid1" >
<DataGrid.Resources>
<Style TargetType="{x:Type Border}" x:Key="coloredBorder">
<Setter Property="Background" Value="Yellow"/>
</Style>
<Style TargetType="DataGridCell">
<Setter Property="IsTabStop" Value="False"/>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding A, Mode=OneWay}" Header="A"/>
<DataGridTemplateColumn Header="B">
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" BorderThickness="0,0,1,0" Width="20" Height="20" Style="{DynamicResource coloredBorder}"/>
<ComboBox Grid.Column="1" SelectedItem="{Binding A, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding MenuItems, ElementName=window}"></ComboBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Also I have appended the example WPF app for this here. To reproduce, clck on one of the comboboxes to give it focus. Pres tab and you'll see that the next combo box gets focus normally. If I added more than one combo box column the combobox to the right would get focus.
BUT if you press enter, the focus would go to the cell below, but it won't then skip the cell and go for the cell content. Instead cell is highlighted.
Like so:
Based on reply I got on MSDN:
I solved this by making an attached property:
using System.Windows;
using System.Windows.Input;
namespace GridTabbing
{
public class EnterKeySample
{
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void ue_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
var ue = e.OriginalSource as FrameworkElement;
if (e.Key == Key.Enter)
{
e.Handled = true;
ue.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
}
}
private static void ue_Unloaded(object sender, RoutedEventArgs e)
{
var ue = sender as FrameworkElement;
if (ue == null) return;
ue.Unloaded -= ue_Unloaded;
ue.PreviewKeyDown -= ue_PreviewKeyDown;
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool),
typeof(EnterKeySample), new UIPropertyMetadata(false, IsEnabledChanged));
static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ue = d as FrameworkElement;
if (ue == null) return;
if ((bool)e.NewValue)
{
ue.Unloaded += ue_Unloaded;
ue.PreviewKeyDown += ue_PreviewKeyDown;
}
else
{
ue.PreviewKeyDown -= ue_PreviewKeyDown;
}
}
}
}
The weird bit is that the only thing this is doing is telling WPF to pass focus DOWN when user presses Enter, which I would have assumed was already happening but obviously isn't.

How to fire Click-Event in Templated-Button

I have searched for this issue but didn't found any Solutions.
I have a Templated-Button with an Image in Button.Template. This Button is Part of a CustomControl.
<Button x:Name="PART_DELETESEARCHBUTTON"
Style="{StaticResource CustomDeleteButtonStyle}"
Command="{x:Static local:CustomSearchControl.DeleteCommand}"
Width="20" Height="20"
Margin="0,0,5,0">
<Button.Template>
<ControlTemplate>
<Image x:Name="PART_IMGDELETE"
Source="{DynamicResource _Av_PinClose_Dark}"
Cursor="Hand"
Margin="2"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
In Class of the CustomControl the Command for the Button is implemented:
static CustomSearchControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomSearchControl),
new FrameworkPropertyMetadata(typeof(CustomSearchControl)));
CommandManager.RegisterClassCommandBinding(typeof(CustomSearchControl),
new CommandBinding(CustomSearchControl.DeleteCommand, C_DeleteCommand));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
static void C_DeleteCommand(object sender, ExecutedRoutedEventArgs e)
{
CustomSearchControl mycontrol = sender as CustomSearchControl;
mycontrol.SearchText = "";
}
public static readonly ICommand DeleteCommand = new RoutedUICommand("DeleteCommand", "DeleteCommand",
typeof(CustomSearchControl),
new InputGestureCollection(new InputGesture[] { new KeyGesture(Key.Enter), new MouseGesture(MouseAction.LeftClick) }));
Now, if MouseClick on Button (Image) the Command isn't fired. When removing Button.Template with Image, all works fine.
How can the MouseClick on the Templated.ButtonImage binded to the Command?
Or is there annother way to solve this?
And secondly: The DeleteCommand clears a TextBox in this CustomControl. That works, but after Clearing, the TextBox lost the Focus. What is to do that the TextBox gets the Focus again after Click on Button??? Trigger or so???
I can't recreate the problem with the command not executing. It's working fine for me.
For the focus issue, the button gets focus when it's clicked. You'll have to explicitly set focus back to the textbox. That's easy. If it doesn't have a name in the template, give it one; I called mine "PART_SearchTextBox" but you can substitute your own name.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_PART_SearchTextBox = (TextBox)GetTemplateChild("PART_SearchTextBox");
}
public void ClearSearchText()
{
SearchText = "";
_PART_SearchTextBox.Focus();
}
private TextBox _PART_SearchTextBox;
static void C_DeleteCommand(object sender, ExecutedRoutedEventArgs e)
{
CustomSearchControl mycontrol = sender as CustomSearchControl;
mycontrol.ClearSearchText();
}
And in the CustomSearchControl template, name the textbox PART_SearchTextBox:
<TextBox
x:Name="PART_SearchTextBox"
Text="{Binding SearchText, RelativeSource={RelativeSource TemplatedParent}}"
...etc...
/>

WPF Since Canvas.SetLeft be used to move items around a canvas in an itemscontrol?

So I originally was going to just throw marker classes on the canvas manually in code behind, but then I thought, hmm, I shouldn't really be throwing actual viewobjects into the code inside my view model. So then I thought, "Let me just have a collection and then add to it when i want the View to update."
However, I'm having issues. The problem I am having is that the line I wish to display is not appearing at the desired location.
My goal was to have a small thumb control that I had customized with a template that I could then place in a canvas and use the canvas.setLeft(double) function to set the position of the thumb. This worked fine when I placed it inside a canvas. However, as soon as I switched to the following itemscontrol, instead of displaying the line at the desired location, it always displays the line at coordinate x = 0.
When I call canvas.getleft() on the thumb control, it returns the desired coordinate. However, that is not where it is displaying. I have created a test case to narrow down outside factors and figure out what is going on.
<Application.Resources>
<Style x:Key="CanvasMarkerStyle" TargetType="{x:Type Comp:CanvasMarker}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="StrokeThickness" Value="3"/>
<Setter Property="Width" Value="3"/>
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualHeight}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Comp:CanvasMarker}">
<Line
Stroke="Orange"
StrokeThickness="{Binding RelativeSource={RelativeSource AncestorType={x:Type Comp:CanvasMarker}},
Path=StrokeThickness}"
X1="0"
Y1="0"
X2="0"
Y2="{Binding RelativeSource={RelativeSource AncestorType={x:Type Comp:CanvasMarker}}, Path=ActualHeight}"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
and the Itemscontrol:
<ItemsControl
x:Name="ItemsControl"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.ZIndex="1"
Background="Gray"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas
x:Name="MarkerCanvas"
SnapsToDevicePixels="false"
/>
</ItemsPanelTemplate>
<Comp:CanvasMarker
MarkerTime="50"
/>
</DataTemplate>
On Loaded, I take MarkerTime and Canvas.SetLeft(this, MarkerTime);
I feel like there is an obvious reason why I cannot use canvas.setleft. My guess is that it has something to do with how canvas manages its attached properties. I am guessing that it uses them during layout to place the child correctly. I think that maybe in my case where the attached.left property is changed inside an itemspanel, it doesn't prompt another layout or something. Anyway, an explanation or suggestions would be greatly appreciated.
I figured doing it this way was much more MVVM than just creating view items on a canvas manually from my view model with the correct properties that i desired.
but having a control so I can bind an items collection seems to be more of a headache than i anticipated.
public CanvasMarker()
{
this.Unloaded += new RoutedEventHandler(CanvasMarker_Unloaded);
this.Initialized += new EventHandler(CanvasMarker_Initialized);
this.Loaded += new RoutedEventHandler(CanvasMarker_Loaded);
this.DragStarted += new DragStartedEventHandler(CanvasMarker_DragStarted);
this.DragDelta += new DragDeltaEventHandler(CanvasMarker_DragDelta);
this.PreviewMouseDown += new System.Windows.Input.MouseButtonEventHandler(CanvasMarker_PreviewMouseDown);
this.Style = (Style)Application.Current.Resources["CanvasMarkerStyle"];
}
void CanvasMarker_Loaded(object sender, RoutedEventArgs e)
{
gvvm = Tag as GraphViewerViewModel;
var p = VisualTreeUtilities.FindParent<DependencyObject>(this);
var gp = VisualTreeUtilities.FindParent<DependencyObject>(p);
var ggp = VisualTreeUtilities.FindParent<DependencyObject>(gp);
var gggp = VisualTreeUtilities.FindParent<DependencyObject>(ggp);
ParentCanvas = VisualTreeUtilities.FindParent<Canvas>(this) as Canvas;
double MarkerHorizontalPositionInPixels = MarkerTime / gvvm.UnitsOfTimePerPixel;
SetMarker(MarkerHorizontalPositionInPixels);
}
void CanvasMarker_Initialized(object sender, EventArgs e)
{
}
void CanvasMarker_Unloaded(object sender, RoutedEventArgs e)
{
}
static void SetMarkerToNewPosition(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CanvasMarker cm = d as CanvasMarker;
if (cm.IsLoaded)
{
double MarkerHorizontalPositionInPixels = cm.MarkerTime / cm.gvvm.UnitsOfTimePerPixel;
cm.SetMarker(MarkerHorizontalPositionInPixels);
if (cm.IsCurrentMarker)
{
cm.gvvm.MarkerExists = true;
cm.gvvm.CalculateValuesAtPrimaryMarker();
}
}
}
void CanvasMarker_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
}
void CanvasMarker_DragDelta(object sender, DragDeltaEventArgs e)
{
double NewXPosition = XPositionInPixels + e.HorizontalChange;
SetMarker(NewXPosition);
}
void SetMarker(double PositionInPixels)
{
if (PositionInPixels < StrokeThickness / 2)
{
PositionInPixels = StrokeThickness / 2;
}
else if (PositionInPixels > ParentCanvas.ActualWidth)
{
PositionInPixels = ParentCanvas.ActualWidth;
}
Canvas.SetLeft(this, PositionInPixels);
double d = Canvas.GetLeft(this);
XPositionInPixels = PositionInPixels;
}
void CanvasMarker_DragStarted(object sender, DragStartedEventArgs e)
{
}

How do I correctly bind a Popup to a ToggleButton?

I am trying to do something that seems relatively simple and logic from a user interface level but I have one bug that is very annoying. I have a ToggleButton and I am trying to show a Popup when the button is toggled in and hide the Popup when the button is toggled out. The Popup also hides when the user clicks away from it.
Everything is working as expected with the following XAML except when I click the toggle button after the Popup is shown, the Popup disappears for a split second then reappears.
I suspect what's going on here is that clicking away from the Popup is causing it to toggle the button off then immediately after the button is toggled back on as the mouse clicks it. I just don't know how to go about fixing it.
Any help is appreciated. Thanks.
<ToggleButton x:Name="TogglePopupButton" Content="My Popup Toggle Button" Width="100" />
<Popup StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=TwoWay}">
<Border Width="100" Height="200" Background="White" BorderThickness="1" BorderBrush="Black">
<TextBlock>This is a test</TextBlock>
</Border>
</Popup>
Stephans answers has the disadvantage, that the desired behaviour of closing the popup whenever it loses focus also disappears.
I solved it by disabling the toggle-button when the popup is open. An alternative would be to use the IsHitTestVisible Property instead of is enabled:
<ToggleButton x:Name="TogglePopupButton" Content="My Popup Toggle Button" Width="100" IsEnabled="{Binding ElementName=ToggledPopup, Path=IsOpen, Converter={StaticResource BoolToInvertedBoolConverter}}"/>
<Popup x:Name="ToggledPopup" StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=TwoWay}">
<Border Width="100" Height="200" Background="White" BorderThickness="1" BorderBrush="Black">
<TextBlock>This is a test</TextBlock>
</Border>
</Popup>
The converter looks like this:
public class BoolToInvertedBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
bool boolValue = (bool)value;
return !boolValue;
}
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("ConvertBack() of BoolToInvertedBoolConverter is not implemented");
}
}
Solution without IValueConverter:
<Grid>
<ToggleButton x:Name="TogglePopupButton" Content="My Popup Toggle Button" Width="100" >
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="IsHitTestVisible" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Popup, Path=IsOpen}" Value="True">
<Setter Property="IsHitTestVisible" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Popup StaysOpen="false" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=TwoWay}"
PlacementTarget="{Binding ElementName=TogglePopupButton}" PopupAnimation="Slide"
x:Name="Popup">
<Border Width="100" Height="200" Background="White" BorderThickness="1" BorderBrush="Black">
<TextBlock>This is a test</TextBlock>
</Border>
</Popup>
</Grid>
I faced the same problem. None of the answers offered here worked correctly.
After a little research, I can say that the suspicions of the author of the question are correct. During a mouse click, the first click (down) closes the popup and set togglebutton as unchecked, the second click (up) causes the observed action when the popup appears again.
The first way to avoid this problem is to discard the second click by delay:
<ToggleButton x:Name="UserPhotoToggleButton"/>
<Popup x:Name="UserInfoPopup"
IsOpen="{Binding IsChecked, ElementName=UserPhotoToggleButton, Delay=200, Mode=TwoWay}"
StaysOpen="False">
It looks simple enough to fix problem. Although it is not an ideal solution. The best way would be to extend the functionality of the popup by Behavior:
Add these namespaces
xmlns:behaviors="clr-namespace:WpfClient.Resources.Behaviors;assembly=WpfClient.Resources"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
then extend your popup by i:Interaction.Behaviors
<Popup x:Name="UserInfoPopup"
StaysOpen="False">
<i:Interaction.Behaviors>
<behaviors:BindToggleButtonToPopupBehavior
DesiredToggleButton="{Binding ElementName=UserPhotoToggleButton}"/>
</i:Interaction.Behaviors>
<Border>
<!--Your template-->
</Border>
</Popup>
Finally add the behavior. In a minimal form, it may look like this:
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace WpfClient.Resources.Behaviors
{
public class BindToggleButtonToPopupBehavior : Behavior<Popup>
{
public ToggleButton DesiredToggleButton
{
get { return (ToggleButton)GetValue(DesiredToggleButtonProperty); }
set { SetValue(DesiredToggleButtonProperty, value); }
}
public static readonly DependencyProperty DesiredToggleButtonProperty =
DependencyProperty.Register(nameof(DesiredToggleButton), typeof(ToggleButton), typeof(BindIconToggleButtonToPopupBehavior), new PropertyMetadata(null));
protected override void OnAttached()
{
base.OnAttached();
DesiredToggleButton.Checked += DesiredToggleButton_Checked;
DesiredToggleButton.Unchecked += DesiredToggleButton_Unchecked;
AssociatedObject.Closed += AssociatedObject_Closed;
AssociatedObject.PreviewMouseUp += AssociatedObject_PreviewMouseUp;
}
private void DesiredToggleButton_Unchecked(object sender, RoutedEventArgs e) => AssociatedObject.IsOpen = false;
private void DesiredToggleButton_Checked(object sender, RoutedEventArgs e) => AssociatedObject.IsOpen = true;
private void AssociatedObject_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.Source is Button)
AssociatedObject.IsOpen = false;
}
private void AssociatedObject_Closed(object sender, EventArgs e)
{
if (DesiredToggleButton != Mouse.DirectlyOver)
DesiredToggleButton.IsChecked = false;
}
protected override void OnDetaching()
{
base.OnDetaching();
DesiredToggleButton.Checked -= DesiredToggleButton_Checked;
DesiredToggleButton.Unchecked -= DesiredToggleButton_Unchecked;
if (AssociatedObject != null)
{
AssociatedObject.Closed -= AssociatedObject_Closed;
AssociatedObject.PreviewMouseUp -= AssociatedObject_PreviewMouseUp;
}
}
}
}
On the ToggleButton set the Property ClickMode="Press"apixeltoofar
Set StaysOpen="True" for your Popup
From MSDN:
Gets or sets a value that indicates whether the Popup control closes
when the control is no longer in focus.
[...]
true if the Popup control closes when IsOpen property is set to false;
false if the Popup control closes when a mouse or keyboard event occurs outside the Popup control.

How to Access a Button present inside a Custom Control, from the implementing page?

I have my generic.xaml containing the following code:
<ControlTemplate TargetType="local:customVideoControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="600"/>
<RowDefinition Height="200"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<MediaElement x:Name="customMediaPlayer" Source="{TemplateBinding CustomMediaSource}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Grid.Row="0" Grid.ColumnSpan="3"
/>
<ToggleButton x:Name="playPauseBtn" Height="50" Width="50" Content="Pause" Grid.Row="1" Grid.Column="0"/>
<Button x:Name="prevBtn" Height="50" Width="50" Content="Prev" Grid.Row="1" Grid.Column="1"/>
<Button x:Name="nextBtn" Height="50" Width="50" Content="Next" Grid.Row="1" Grid.Column="2"/>
</Grid>
</ControlTemplate>
Now on applyTemplate , I am accessing the controls like below:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ToggleButton playPauseBtn = GetTemplateChild("playPauseBtn") as ToggleButton;
Button prevBtn= GetTemplateChild("prevBtn") as Button;
Button nextBtn = GetTemplateChild("nextBtn") as Button;
MediaElement customMediaPlayer = GetTemplateChild("customMediaPlayer") as MediaElement;
playPauseBtn.Checked += (obj, Args) =>
{
customMediaPlayer.Pause();
playPauseBtn.Content = "Play";
};
playPauseBtn.Unchecked += (obj, Args) =>
{
customMediaPlayer.Play();
playPauseBtn.Content = "Pause";
};
nextBtn.Click += (obj, Args) =>
{
customMediaPlayer.Source=new Uri(CustomMediaSource.ToString(),UriKind.RelativeOrAbsolute);
};
prevBtn.Click += (obj, Args) =>
{
customMediaPlayer.Source = new Uri(CustomMediaSource.ToString(), UriKind.RelativeOrAbsolute);
};
}
Now I want acccess the nextBtn, in the page where I am implementing like
CustomVideoControl myVControl=new CustomVideoControl();
This will create the instance of the control, but I want to do something on the click of
next and previous button, thta is present inside the CustomVideoControl in generic.xaml. Any help will be greatly appreciated.
Thanks,
Subhen
You just need to add a couple of events to your Control.
public event EventHandler MovedPrevious
public event EventHandler MovedNext
Now this are typically implemented like this:-
protected virtual void OnMovedPrevious(EventArgs e)
{
var handler = MovedPrevious;
if (handler != null)
handler(this, e);
}
protected virtual void OnMovedNext(EventArgs e)
{
var handler = MovedNext;
if (handler != null)
handler(this, e);
}
Now in your existing click events:-
nextBtn.Click += (obj, Args) =>
{
customMediaPlayer.Source=new Uri(CustomMediaSource.ToString(),UriKind.RelativeOrAbsolute); //No idea what this doing
OnMovedNext(EventArgs.Empty);
};
prevBtn.Click += (obj, Args) =>
{
customMediaPlayer.Source = new Uri(CustomMediaSource.ToString(), UriKind.RelativeOrAbsolute); //No idea what this is doing either
OnMovedPrevious(EventArgs.Empty);
};
Now in your consuming code you can do this sort of thing:-
CustomVideoControl myVControl=new CustomVideoControl();
myVControl.MovedNext += (s, args) => { /* deal with next */ };
myVControl.MovedPrevious += (s, args) => { /* deal with previous */ };
Sorry, but you're doing it wrong.
There's no good reason why you should have a reference to elements inside a DataTemplate IMO.
[...Read more at this forum post...]
You could create public events in your custom control, something like NextButtonClicked and PreviousButtonClicked.
I have a feeling, that you're trying to mimic EventSetter behaviour. If I'm right, please just take a look on this simple example:
<Style TargetType="{x:Type Button}" x:Key="SomeID">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
</Trigger>
</Style.Triggers>
<EventSetter Event="MouseUp" Handler="DoSomething_Click"></EventSetter>
</Style>
This code assigns your custom event to some text block's regular action directly from XAML (you don't have to pollute your code behind with accessing controls' properties).
I hope this is helpful, but if not, please give me a shout.
Edit:
Sorry for not being perfectly clear (this was just a quickly pasted code snippet). Please have a look on a complete example:
Styles for your next/previous buttons:
<Style TargetType="{x:Type Button}" x:Key="PreviousButtonstyle">
<EventSetter Event="Click" Handler="OnMovedPrevious"></EventSetter>
</Style>
<Style TargetType="{x:Type Button}" x:Key="NextButtonstyle">
<EventSetter Event="Click" Handler="OnMovedNext"></EventSetter>
</Style>
Code behind:
public event EventHandler MovedPrevious;
public event EventHandler MovedNext;
protected void OnMovedPrevious(object sender, RoutedEventArgs e)
{
if (MovedPrevious != null)
{
MovedPrevious(this, e);
}
}
protected void OnMovedNext(object sender, RoutedEventArgs e)
{
if (MovedNext != null)
{
MovedNext(this, e);
}
}
Since now on you can access OnMovedNext and OnMovedPrevious directly from your control's handling conrol/whatever just as Anthony posted.
Sorry if my previous answer was confusing, but it supposed to be just an inspiration what to do :)
Edit:
I haven't noticed that this regards only Silverlight for which I apologize :) But, it works perfectly for WPF if you wish to try.

Categories

Resources