I have a button with a click method assigned to it. I'd like to move that method to another CS file so I may reuse it in other places. Is it possible in XAML to reference a method, click method, from a CS source file outside of the Pages source file?
<Button x:Name="TheRedButton" Click="DoNotPressTheRedButton_Click" />
I want to locate DoNotPressTheRedButton_Click in another source file.
ButtonMethods.cs
and then use it in my Page from XAML
MyPage.xaml
What do I need to write in XAML in order to do this?
I thought I need:
xmlns:helpermethods="using:CoreProject.HelperMethods"
then,
<Button x:Name="TheRedButton" Click="helpermethods:ButtonMethods.DoNotPressTheRedButton_Click" />
But I'm getting compiler errors... CS1002 and CS1513. So obviously it's not the right syntax. Is this even possible?
Easy peasy:
<Button Click="{x:Bind helpermethods:ButtonMethods.ButtonClick}" />
Or in WPF:
<Button Click="{x:Static helpermethods:ButtonMethods.ButtonClick}" />
C#:
public static class ButtonMethods
{
public static RoutedEventHandler ButtonClick => Button_Click;
public static void Button_Click(object sender, RoutedEventArgs e)
{
// stuff
}
}
You could call your method in ButtonMethods from the click handler of your TheRedButton.
Like this:
private void OnSomebodyClickedTheRedButtonWhenTheyWereNotMeantTo(object sender, RoutedEventArgs args)
{
// do some other stuff with sender - find out which button was clicked, etc.
ButtonMethods.RedButtonClicked(sender, args);
}
This would allow you to also do "other stuff" that you might need, within this class, from this event handler (if you wanted to).
Make sure your method is static, otherwise you would need to (unnecessarily) create an instance of the ButtonMethods class each time.
Related
I am using AvaloniaUI https://avaloniaui.net/docs/
I have researched their docs but it seems I can not find how can I make button which when pressed is forcing you to choose a folder.
Is it even possible and if so how, is there any example ?
I toyed with AvaloniaUI some time ago, got it working under Windows and struggled getting it working under Mac.
Nevertheless, I've seen your other question where you seem to get the dialog opened. Still, for the future:
In your XAML you place a button in a place you please:
<Button Content="Choose folder..." Margin="3" Name="FolderButton" />
Perhaps there is another way of getting it working, the following worked for me:
In your code you need to create a variable that represents your button:
private Button _folderButton;
In your constructor or in your InitializeComponent() method you find the button from XAML and assign it to your variable:
_folderButton = this.FindControl<Button>("FolderButton");
You also assign an event handler for Click event:
_folderButton.Click += FolderButtonClick;
You can immediately add the unsubscribe in your destructor:
_folderButton.Click -= FolderButtonClick;
Now you provide an event handler declaration and implementation:
public void FolderButtonClick(object sender, RoutedEventArgs e)
{
...
}
You may use http://avaloniaui.net/api/Avalonia.Controls/OpenFolderDialog/ - as you've already found out in your other question.
This even handler can be made async if you have any await operations inside.
I hope this helps.
I'm developing a Windows 10 UWP app and I'd like to add textboxes with buttons like shown in the screenshot. The screenshot is taken from the universal Maps app on Windows 10. If someone could point me to any documentation or samples, I'd be very grateful.
Here's a hint: look at the TextBox control template.
It's very long so I won't copy the whole thing, but if you look at the bottom, you'll notice that there is a ContentControl for placeholder text (PlaceholderTextContentPresenter), and there's also a button called DeleteButton which is visible when you focus on a TextBox with some text inside.
This shows you that one way to do what you want is to modify the template to have another button next to the DeleteButton or something similar (depending on what you're trying to achieve in the end) and then you can hide it or show it depending on the current VisualState, which is also something you would define in your template.
Making a template also means that you can make it quite reusable, so you should be able to use it in future projects, too.
This answers the follow-up question: How do you handle the events on the extra button?
If your new button template resides in some local resource dictionary (e.g., Page.Resources, you can just add a click handler directly. If, however, the button template lives in a separate resource dictionary (Styles.xaml or whatever), doing so would require creating code-behind for the dictionary. It is probably better to subclass and do something like this:
public class MyTextBox : TextBox
{
public event EventHandler<RoutedEventArgs> ExtraClick;
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
Button extraButton = GetTemplateChild("ExtraButton") as Button;
if (extraButton != null)
{
extraButton.Click += (sender, e) => ExtraClick?.Invoke(sender, e);
}
}
}
You could then handle this (or any other you care to tackle) events where you actually use the button, e.g.,
<local:MyTextBox ExtraClick="OnExtraClicked" />
Code-behind:
private void OnExtraClicked(object sender, RoutedEventArgs e)
{
// ...
}
For completeness: The external dictionary must be merged into the available resources at some point, for example in the application's resources:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
I have a PlayerV.xaml View with a Slider inside:
<Slider Value="{Binding CurrentProgress}"/>
and have a button:
<Button Content="next song" Command="{Binding playNext}"/>
Button works correct. Button's playNext command is contained in PlayerVM.cs
I want slider's ValueChanged to call a function which is stored in PlayerVM.cs:
[1]:<Slider Value="{Binding CurrentProgress}" ValueChanged="{Binding playNext}"/>
I know [1] has an incorrect syntax, I used it for sake of clarity of explanation.
====Additional Explanation====
I know I can write:
<Slider ValueChanged="Slider_ValueChanged" Value="{Binding CurrentProgress}" />
And in PlayerV.xaml.cs there will be
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
//some logic (actions)
}
}
But I don't want any logic in there. I want it to be in PlayerVM.cs (like button's command handler functions).
How to do that?
Also in my App.xaml.cs startup function is:
private void OnStartup(object sender, StartupEventArgs e)
{
MainWindow _mainWindow = new MainWindow();
PlayerVM playerVM = new PlayerVM();
_mainWindow.DataContext = playerVM;
_mainWindow.Show();
}
You have two options. First, despite what you said about not wanting to use code behind, one solution is for you to do just that. In the ValueChanged event handler, you can simply call your view model method when ever the value is changed:
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Slider slider = sender as Slider;
PlayerVM viewModel = (PlayerVM)DataContext;
viewModel.YourMethod(slider.Value);
}
I've offered this solution because I suspect that you're new to MVVM and still think that you're not allowed to use the code behind. In fact, that is not the case at all and for purely UI matters such as this, it's a good place for it.
Another option is just to data bind a property directly to the Slider.Value. As the Slider value is changed, so will the property be. Therefore, you can simply call your method from the data bound property setter:
public double CurrentProgress
{
get { return currentProgress; }
set
{
currentProgress = value;
NotifyPropertyChanged("CurrentProgress");
YourMethod(value);
}
}
One further option involves handling the ValueChanged event in a custom Attached Property. There is a bit more to this solution than the others, so I'd prefer to direct you to some answers that I have already written for other questions, rather than re-writing it all again. Please see my answers to the How to set Focus to a WPF Control using MVVM? and WPF C# - navigate WebBrowser on mouseclick through Binding questions for explanations and code examples of this method.
By using the event to commend logic you can bind events to your view model, but you need help. You need to use functions from the System.Windows.Interactivity Namespace and include a MVVM Light (there might be other MVVM libraries that have that feature but i use MVVM Light).
refer this: Is it possible to bind a WPF Event to MVVM ViewModel command?
My scenario is quite simple, in fact I thought it would cause no problem but it does, can anyone help?
My MainPage begins with this:
namespace PhoneApp
{
public partial class MainPage : PhoneApplicationPage { /* snip */ }
}
Later in the page is this simple method:
private void MakeSound(object sender, MouseButtonEventArgs e)
All right, everything is fine.
But on my SettingsSample.xaml I have a ListBoxItem that calls the method MakeSoundvia SelectionChanged property. VS studio tells me that 'event handler MakeSoundis not found on class PhoneApp.SettingsSample '
The code for this page begins with:
namespace PhoneApp
{
public partial class SettingsSample : PhoneApplicationPage { /* snip */ }
}
Copying the code of the method on SettingsPage.xaml.cs doesn't work for some reason, but I feel like it should possible to use the method described on MaingePage.xaml.cs, especially since their respective code resides in the same Namespace.
I tried to add
using PhoneApp;
on SettingsSample.xaml.cs. Not exceptionally clever, but I have no other idea
Your event function should read EventClickOnMyListBox and be located in the code-behind for your page. This function would then call your makeasound function (that you can put wherever you want). And it could be called by another event or function.
There is a semantics distinction between the event itself and what the event does. Your event is not makeasound, your event will cause a sound to be made, among possible other things.
If ever you want to add a popup to add a visual effect, you'll be stuck if you called directly the makeasound function, whereas you'll just have to add a line to your EventClickOnMyListBox in the other scenario.
That's why xaml works that way, and allows you to refer only to object in the enclosing class (or if there is a way I don't know it / and never needed it). You should minimize code-behind code (this is the way of thinking of the MVVM pattern if you want to go further down this road).
Replying to your comment, here is some code to get you started:
Your event should be in the code-behind of your SettingSample page, and should call the makeasound function instead of being the makeasound function:
private void MenuItem_Click(object sender, MouseButtonEventArgs e)
{
Whatever.Myclass.MakeASound (blabla); // So typically MakeASound would be static
}
And now same thing in your main page: you do not use your private void MakeSound(object sender, MouseButtonEventArgs e) as the event for a click, but an event handler just as the one above.
Take a step back and you'll see it's very natural: you want two things to do the same. But one thing cannot be the exact same thing as the first, for language-related reasons (read about C# and XAML to understand why).
So you put your feature in a third component, accessible from the first two. And you call this component from each of them. This way, they both share access to the same feature, and everybody is happy.
I need to drop an image file into my WPF application. I currently have a event firing when I drop the files in, but I don't know how what to do next. How do I get the Image? Is the sender object the image or the control?
private void ImagePanel_Drop(object sender, DragEventArgs e)
{
//what next, dont know how to get the image object, can I get the file path here?
}
This is basically what you want to do.
private void ImagePanel_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
// Note that you can have more than one file.
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
// Assuming you have one file that you care about, pass it off to whatever
// handling code you have defined.
HandleFileOpen(files[0]);
}
}
Also, don't forget to actually hook up the event in XAML, as well as setting the AllowDrop attribute.
<StackPanel Name="ImagePanel" Drop="ImagePanel_Drop" AllowDrop="true">
...
</StackPanel>
The image file is contained in the e parameter, which is an instance of the DragEventArgs class.
(The sender parameter contains a reference to the object that raised the event.)
Specifically, check the e.Data member; as the documentation explains, this returns a reference to the data object (IDataObject) that contains the data from the drag event.
The IDataObject interface provides a number of methods for retrieving the data object that you're after. You'll probably want to start by calling the GetFormats method in order to find out the format of the data that you're working with. (For example, is it an actual image or simply the path to an image file?)
Then, once you've identified the format of the file being dragged in, you'll call one of the specific overloads of the GetData method to actually retrieve the data object in a particular format.
Additionally to answer of A.R. please note that if you want to use TextBox to drop you have to know following stuff.
TextBox seems to have already some default handling for DragAndDrop. If your data object is a String, it simply works. Other types are not handled and you get the Forbidden mouse effect and your Drop handler is never called.
It seems like you can enable your own handling with e.Handled to true in a PreviewDragOver event handler.
XAML
<TextBox AllowDrop="True" x:Name="RtbInputFile" HorizontalAlignment="Stretch" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" />
C#
RtbInputFile.Drop += RtbInputFile_Drop;
RtbInputFile.PreviewDragOver += RtbInputFile_PreviewDragOver;
private void RtbInputFile_PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
private void RtbInputFile_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
// Note that you can have more than one file.
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
var file = files[0];
HandleFile(file);
}
}