How to call method in a different namespace from XAML - c#

I am building a desktop application with WPF and want to open a hyperlink in a browser. I can do this by putting a method in the code behind and calling it from the XAML as follows, but how can I call this method from multiple XAML pages?
XAML
<Hyperlink NavigateUri="http://www.mylink.com" RequestNavigate="Hyperlink_RequestNavigate">My link text</Hyperlink>
C#
private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}

You could put this into a style in App.xaml, e.g.
<Application.Resources>
<Style x:Key="LaunchLinkStyle" TargetType="{x:Type Hyperlink}">
<EventSetter Event="RequestNavigate" Handler="LaunchLinkStyle_RequestNavigate" />
</Style>
</Application.Resources>
(The handler then of course would be implemented in App.xaml.cs)
You then can just reference the style:
<Hyperlink Style="{StaticResource LaunchLinkStyle}" ... />

Thanks H.B. Your answer set me on the right path. Here's the complete code:
In my page:
<Hyperlink NavigateUri="http://www.mylink.com" Style="{StaticResource LaunchLinkStyle}">My Link</Hyperlink>
App.xaml
<Style x:Key="LaunchLinkStyle" TargetType="{x:Type Hyperlink}">
<EventSetter Event="RequestNavigate" Handler="LaunchLinkStyle_RequestNavigate"/>
</Style>
App.xaml.cs
public void LaunchLinkStyle_RequestNavigate(object sender, RoutedEventArgs e)
{
/* Function loads URL in separate browser window. */
Hyperlink link = e.OriginalSource as Hyperlink;
Process.Start(link.NavigateUri.AbsoluteUri);
e.Handled = true; //Set this to true or the hyperlink loads in application and browser windows
}

Related

MouseLeftButtonUp does not fire for ScrollViewer in WPF control template

In WPF and C# I am trying to set up a mouse drag scrolling feature for a ScrollViewer contained within a control template for a document viewer. The problem: I have not been able to get the MouseLeftButtonEvent to fire.
It is basically the default DocumentViewer template, with a few features modified. Here is an outline in XAML:
<Style x:Key="DocumentViewerStyle1" BasedOn="{x:Null}" TargetType="{x:Type DocumentViewer}">
<!--...—>
<Setter Property="ContextMenu" Value="{x:Null}" /> <!--So does not mess up right click, if I use that-->
<!--...-->
<ControlTemplate TargetType="{x:Type DocumentViewer}">
<!--...-->
<ScrollViewer x:Name="PART_ContentHost" CanContentScroll="True"
IsHitTestVisible="True" HorizontalScrollBarVisibility="Auto"
Grid.Row="1" Loaded ="OnScrollViewerLoaded" />
<DockPanel Grid.Row="1" >
<!-...-->
</ControlTemplate>
</Style>
I use the following in code behind so that I can access the ScrollViewer. If one changes “Left” to “Right” in the method below, it works to perfection, but of course with the right mouse button rather than the left.
public partial class PrintPreview : Window
{
private ScrollViewer nomad;
etc. and
private void OnScrollViewerLoaded(object sender, RoutedEventArgs e)
{
nomad = (ScrollViewer)sender;
nomad.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseButtonDown), true);
nomad.AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseButtonUp), true);
nomad.AddHandler(MouseMoveEvent, new MouseEventHandler(OnMouseMove), true);
}
The OnMouseButtonUp event handler, for example, is
private void OnMouseButtonUp(object sender, MouseButtonEventArgs e)
{
nomad.Cursor = Cursors.IBeam;
nomad.ReleaseMouseCapture();
}
Have tried various things found here: No help from using Preview events for my three mouse events. No help from setting Focusable="False" for the ScrollViewer, or for setting a Background for it. Any suggestions? Thanks!

Xamarin Forms WebView cache: GoBack() doesn't call Navigating event

As I previously said in this question, I'm new at Xamarin.Forms and I'm developing a cross-platform web browser with Microsoft Visual Studio 2017 version 15.5.4. I'm debugging on an Android 5.1 smartphone.
In my layout there's a WebView and two Buttons to go back/forward.
<Button Image="backarrowdisabled.png"
Grid.Row="1"
Grid.Column="0"
x:Name="backButton"
IsEnabled="False"
Clicked="previousPage">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding CanGoBack, Source={Reference appWebView}}"
Value="False">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Image" Value="backarrowdisabled.png" />
</DataTrigger>
<DataTrigger TargetType="Button"
Binding="{Binding CanGoBack, Source={Reference appWebView}}"
Value="True">
<Setter Property="IsEnabled" Value="True" />
<Setter Property="Image" Value="backarrow.png" />
</DataTrigger>
</Button.Triggers>
</Button>
<Button Image="nextarrowdisabled.png"
Grid.Row="1"
Grid.Column="5"
x:Name="nextButton"
IsEnabled="False"
Clicked="nextPage">
/* triggers */
</Button>
<WebView Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="6"
x:Name="appWebView"
Source="https://www.google.it/"
Navigating="onPageLoading"
Navigated="onPageLoaded"/>
As you can see there are two methods to manage Navigating and Navigated events, which are
private void onPageLoading(object sender, WebNavigatingEventArgs e)
{
URLEntry.Text = e.Url; //Entry where I can see the page URL
//other code
}
private void onPageLoaded(object sender, EventArgs e)
{
//some code
}
When I click on a link or something like that in my WebView everything works correctly, but when I have to go back/forward in my history the Entry doesn't update the URL to the previous/next one. It seems it doesn't even call the onPageLoading method, but the WebView loads the page.
These are the methods called by the Buttons
private void previousPage (object sender, EventArgs e)
{
appWebView.GoBack();
}
private void nextPage (object sender, EventArgs e)
{
appWebView.GoForward();
}
Is it a WebView bug or am I doing something wrong?
Edit: I think the pages are cached, is there a way to make it not do it?
One thing you can do, is to create another event handler for the WebView's PropertyChanged or PropertyChanging event. There, you can check to see if the Source property was changed; If so, you can update the TextView with the new source. (I'm not sure exactly, but it seems like whe navigating back or forward, the Navigating event does not get called, possibly because the page is already cached?)
See here: https://developer.xamarin.com/api/member/Xamarin.Forms.WebView.OnPropertyChanged/p/System.String/
So your code can look like
<WebView x:Name="MyWebView" PropertyChanged="OnWebViewPropertyChanged" />
and then your code behind:
private void OnWebViewPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == WebView.SourceProperty.PropertyName)
{
URLEntry.Text = MyWebView.Source.ToString(); // May need to check this
}
}

Setting button flat style programmatically

I want to give a button a flat style programmatically when certain conditions occur.
This question shows how I can set a style to a control programmatically, having already defined it in XAML.
This question shows that a flat button style already exists, so it is not necessary to create one in XAML.
ToolBar.ButtonStyleKey returns a ResourceKey, and the corresponding style is not defined in my window (it's in ToolBar). How do I use it in code to set the style programmatically?
As an alternative, you can try this:
XAML
<Button Name="FlatButton" Width="100" Height="30" Content="Test" />
Code behind
private void Button_Click(object sender, RoutedEventArgs e)
{
FlatButton.Style = (Style)FindResource(ToolBar.ButtonStyleKey);
}
This is a workaround that works. Add a style based on ToolBar.ButtonStyleKey to Window.Resources as follows:
<Window.Resources>
<Style x:Key="MyStyle" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" TargetType="Button" />
</Window.Resources>
Then, in code, refer to it as per first link in this question:
button.Style = this.Resources["MyStyle"] as Style;
I'd prefer to have a code-only solution (no XAML) for this, but this works just as well.

Strange behaviour of overridden Style of TextBlock

Some days ago I've faced with strange behaviour of text inside Button (I guess the same behaviour I would got for other ContentControls). Let me explain the situation. I have a style definition in App.xaml for TextBlock:
<Application.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="10"/>
</Style>
</Application.Resources>
In MainWindow.xaml I have the same style definition, that should override style that defined in App.xaml. Also I have 3 buttons in Window. In first button explicitly defined TextBlock control inside button's content. For second button I set a string as content in codebehind. For third button I set an integer value as content in codebehind. Here is code of MainWindow.xaml:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="0"/>
</Style>
</StackPanel.Resources>
<Button Name="Button1">
<Button.Content>
<TextBlock Text="Button with text block"/>
</Button.Content>
</Button>
<Button Name="Button2" />
<Button Name="Button3" />
</StackPanel>
and MainWindow.xaml.cs:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Button2.Content = "Button with string";
Button3.Content = 16;
}
And now what we see? Text in first and third buttons, as expected, have margins 0px, but text in second button have margins 10px! The question is: why second button has 10px margins and how set zero margins for second button (removing style from App.xaml is not possible)?
Thank you!
When I change
Button2.Content = "Button with string";
to
Button2.Content = "Button with _string";
the button's margin changes from 10 to 0.
This is a bug in WPF; it already has been reported on Microsoft Connect.
I am not 100% sure but I think the behavior you saw is caused by the same root cause.
By the way: the correct behavior would be that buttons 2 and 3 have Margin=10; this is because resource lookup is performed along the logical tree, not along the visual tree. The TextBlocks in the buttons 2 and 3 are not inside the logical tree of the StackPanel.
I can't give you a definitive answer, but I notice that it's the difference between setting a string and an integer which causes the different styles to be applied.
Since setting Content to a value that requires a conversion results in correct style being applied, I tried this:
private void WindowLoaded(object sender, RoutedEventArgs e)
{
Button2.Content = new TextHolder("Button with string");
Button3.Content = 16;
}
public class TextHolder
{
private readonly string _text;
public TextHolder(string text)
{
_text = text;
}
public override string ToString()
{
return _text;
}
}
and the margin is now 0. I would be interested in understanding exactly what is going on.

Reference to control inside a ControlTemplate

How do I, form my contructor in the code-behind get a reference to the OuterBorder control in the XAML below?
<Window Template="{DynamicResource WindowTemplate}">
<Window.Resources>
<ControlTemplate x:Key="WindowTemplate" TargetType="{x:Type Window}">
<AdornerDecorator>
<Border Name="OuterBorder" Background="Black" BorderBrush="Red" BorderThickness="1" CornerRadius="0">
<!-- Implementation here... -->
</Border>
</AdornerDecorator>
</ControlTemplate>
</Window.Resources>
</Window>
Two possible solutions:
Solution 1
Put a Loaded event in XAML
<Border Name="OuterBorder" Loaded="Border_Loaded" ...
And in code behind store it in a private field:
private Border border;
void Border_Loaded(object sender, RoutedEventArgs e)
{
this.border = (Border)sender;
}
OR:
Solution 2
Override the OnApplyTemplate of your Window:
private Border border;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.border = (Border) Template.FindName("OuterBorder", this);
}
You may want to reconsider your approach. What are you trying to do?
Generally, you shouldn't want or need to access portions of the ControlTemplate from your codebehind because your template is just that-- a template. It's how the control looks. You want your codebehind to generally affect the behavior of the control.
For example, if you're trying to affect the color of the border in the codebehind in certain interactive situations, you really want to add some (pre .Net4) triggers or (post .Net4) a VisualStateManager to your control template to manage your control's visual states for you.

Categories

Resources