Button inside button (WPF + Caliburn.Micro) - c#

In regular WPF you can have a button inside another button, and to prevent both button events being raised when you click the inner button, you can have the following XAML (As per this question):
<Button Click="OuterClick">
<Grid>
<SomeOtherContent />
<Button Click="InnerClick" />
</Grid>
</Button>
And then in the code-behind use the Handled property on the event so that OuterClick is not raised when the inner button has been clicked:
private void OuterClick(object sender, RoutedEventArgs e) {
// Do something
}
private void InnerClick(object sender, RoutedEventArgs e) {
// Do something else
e.Handled = true;
}
However, following the usual Caliburn.Micro conventions you'll have the following XAML:
<Button x:Name="OuterClick">
<Grid>
<SomeOtherContent />
<Button x:Name="InnerClick" />
</Grid>
</Button>
With this in the ViewModel:
private void OuterClick() {
// Do something
}
private void InnerClick() {
// Do something else
}
How can I make sure that not both InnerClick() and OuterClick() are raised when I click the InnerButton?
EDIT: Or, what other kind of controls can I use instead?

<StackPanel cal:Message.Attach="[Event MouseDown]=[Action OuterClick()]">
<StackPanel Orientation="Horizontal">
<SomeotherContent />
<Button x:Name="InnerClick" />
</StackPanel>
</StackPanel>
In Your viewmodel
public void OuterClick()
{
//Do something
}
public void InnerClick()
{
//Do something
}
use stackpanel and apply event for that one.
This will help you.

Related

Show a sequence of images in WPF then close form

I have a window that shows an open lock.
When the user clicks a button the lock must change to closed, wait a second and then close the windows.
How can I do that using WPF?
Here is my initial xaml:
<Button Grid.Row="2" BorderThickness="0" Background="Transparent" Margin="32"
IsTabStop="False" Click="BtnUnlockClick">
<Button.Content>
<Grid>
<Image Grid.Row="1" Source="/Common.Wpf;component/images/unlocked.png" Visibility="Visible" Name="imgUnlocked"/>
<Image Grid.Row="1" Source="/Common.Wpf;component/images/locked.png" Visibility="Collapsed" Name="imgLocked"/>
</Grid>
</Button.Content>
</Button>
and C#:
private void BtnUnlockClick(object sender, RoutedEventArgs e)
{
//do stuff here
}
Put only one Image element into the Button's Content.
<Button Click="BtnUnlockClick" ...>
<Image Source="/Common.Wpf;component/images/unlocked.png"/>
</Button>
In the Click event handler change its Source, wait a second, then close the Window. The handler method must be declared async.
private async void BtnUnlockClick(object sender, RoutedEventArgs e)
{
var image = (Image)((Button)sender).Content;
image.Source = new BitmapImage(
new Uri("pack://application:,,,/Common.Wpf;component/images/locked.png"));
await Task.Delay(1000);
Close();
}

WPF multiple buttons, same click function but different parameter

I want to make several buttons with the same click function but different parameter. Like this
XAML:
<Button Click="MyClickFunction(1)" Content="MyFunc(1)"/>
<Button Click="MyClickFunction(2)" Content="MyFunc(2)"/>
<Button Click="MyClickFunction(3)" Content="MyFunc(3)"/>
<Button Click="MyClickFunction(4)" Content="MyFunc(4)"/>
C#:
private void MyClickFunction(object sender, RoutedEventArgs e, int myParam)
{
//do something with parameter e.g. MessageBox.Show()
MessageBox.Show(myParam.ToString());
}
How to I pass the parameter in xaml tag or I will have to do it other ways?
Use Tag property in the button. Like this:
<Button Click="MyClickFunction" Tag="1" Content="MyFunc(1)"/>
<Button Click="MyClickFunction" Tag="2" Content="MyFunc(2)"/>
<Button Click="MyClickFunction" Tag="3" Content="MyFunc(3)"/>
<Button Click="MyClickFunction" Tag="4" Content="MyFunc(4)"/>
And in the code behind:
private void MyClickFunction(object sender, RoutedEventArgs e)
{
var tag = ((Button)sender).Tag;
MessageBox.Show(tag.ToString());
}

Mouse Events on WPF Page - On sub controls only?

I am working on a C# WPF Project. The MainWindows holds only a Frame Control that is used to navigate to different Pages.
On one Page I use a Button to show a PopUp. Now I would like to hide this Popup when the Mouse leaves it and moves above the Page.
<Page x:Class="Name.SpaceMainPage"
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"
Title="MainPage"
MouseMove="Page_MouseMove">
<StackPanel>
<Label Content="Some Label"/>
<Label Content="Other Label"/>
<Button Name="PopupButton" Content="Popup" Click="MenuButtonClick" />
<Label Content="Third Label"/>
<Popup Name="ExtrasPopup" PlacementTarget="{Binding ElementName=PopupButton}" Placement="Top" PopupAnimation="Scroll">
<StackPanel Background="LightGray">
...
</StackPanel>
</Popup>
</Grid>
</Page>
public partial class MainPage : Page {
...
private void MenuButtonClick(object sender, RoutedEventArgs e) {
ExtrasPopup.IsOpen = true;
}
private void Page_MouseMove(object sender, MouseEventArgs e) {
System.Diagnostics.Debug.WriteLine("Move: " + e.MouseDevice.GetPosition(this));
}
}
The Problem: The Page_MouseMove is only executed when the mouse moves above of of the child controls (the labels or the button). If the mouse moves above the empty space of the page, the event does not fire.
The same is true for any other mouse event such as MouseDown, MouseEnter, MouseLeave, etc... This is quite strange behaviour. Is this intended or am I making any mistake?
It would be a better idea to register to the MouseLeave event on the Button or whatever control you want to use.
<Button Name="PopupButton" Content="Popup" Click="MenuButtonClick" MouseLeave="Menu_MouseLeave" />
And in the code behind simply hide the popup when mouse leaves the button:
private void Menu_MouseLeave(object sender, MouseEventArgs e)
{
ExtrasPopup.IsOpen = false;
}

How to select an item in a ListBox and pick the item's name from a DataTemplate in WPF

I am implementing a Download UI in WPF, where every file that is being downloaded will be shown inside a list box in a DataTemplate
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="FileName" text={Binding FileName}" />
<ProgressBar ... />
<Button Content="Cancel" click="ButtonCancel_Click" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox>
Now this List is getting populated with all the download information perfectly. Only problem I am having is that when user clicks on Cancel button, to cancel the download, I have to remove an entry from the ObservableCollections. But I don't have the File Name in the click event( I know click event is not MVVM, still I want to do it in click event handler).
Can anyone suggest how do I get the FileName of that particular file when the selectedItem gets cancelled. in The
private void ButtonCancel_Click(...) {}
Although I would still encourage you to use MVVM way of dealing with UI events, here's how you can achieve what you want, using Cancel button's click event handler.
First in your xaml, bind file name to Cancel button's Tag property.
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="FileName" text={Binding FileName}" />
<ProgressBar ... />
<Button Content="Cancel" Tag="{Binding FileName}"
Click="ButtonCancel_Click" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox>
Then in your click event handler
private void ButtonCancel_Click(object sender, RoutedEventArgs e)
{
Button myButton = (Button)sender;
string fileName = myButton.Tag.ToString();
// use fileName
}
Edit
Just to add a complete example, that was tested locally, and ensured that works.
XAML
<Window x:Class="WpfTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Name="listBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="FileName" Text="{Binding Path=FileName}" />
<Button Content="Cancel" Tag="{Binding Path=FileName}"
Click="ButtonCancel_Click" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var fileNames = new List<DownloadModel>
{
new DownloadModel
{
FileName = "File1"
},
new DownloadModel
{
FileName = "File2"
},
new DownloadModel
{
FileName = "File3"
}
};
listBox1.ItemsSource = fileNames;
}
private void ButtonCancel_Click(object sender, RoutedEventArgs e)
{
var myButton = sender as Button;
if (myButton.Tag == null)
{
MessageBox.Show("Tag value was null.");
}
else
{
MessageBox.Show(string.Format("File name is {0}", myButton.Tag));
}
}
}
public class DownloadModel
{
public string FileName { get; set; }
}

Dynamically Adding event handlers to WPF

I'm looking for help with an app I'm building. I've an xml file being read into an app. This XML is of the following structure:
`<Tabs>
<Tab>
<Search name="ListSearch" Title="SearchHeader">
<Label Name="lblSchema"></Label>
<ComboBox Name="comboxSchema" Visibility="Visible" IsEnabled="True" ItemSource="{Binding AvailableSchema}" SelectedValue="{Binding SelectedSchema}" />
<ComboBox Name="comboxList" Visibility="Visible" IsEnabled="True" ItemSource="{Binding AvailableList}" SelectedValue="{Binding SelectedList}" />
<Label Name="lblCriteria"></Label>
<ComboBox Name="comboxFields" Visibility="Visible" IsEnabled="True" ItemSource="{Binding AvailableFields}" SelectedValue="{Binding SelectedField}" />
<ComboBox Name="comboxOperator" Visibility="Visible" IsEnabled="True" ItemSource="{Binding Operations}" SelectedValue="{Binding SelectedOperator}" />
<TextBox Name="txtBoxInputValue" Visibility="Visible" IsEnabled="True" Text="{Binding InputValue}" />
<CustomControl type="DatePicker"></CustomControl>
<Button Name="btnAddQueryLine" Content="{Binding TextOnAddQueryButton}" Command="{Binding CamlAddQueryLine}" Action="Publish"></Button>
<Button Name="btnPasteQueryLine" Content="{Binding TextOnPasteQueryButton}" Command="{Binding CamlPasteQueryLine}" Action="Preview"></Button>
<Button Name="btnRemoveQueryLine" Content="{Binding TextOnRemoveQueryButton}" Command="{Binding CamlRemoveQueryLine}" Action="UnPublish"></Button>
<Button Name="btnClearQuery" Content="{Binding TextOnClearQueryButton}" Command="{Binding CamlClearQuery}" Action="UnPreview"></Button>
<Button Name="btnCamlSearch" Content="{Binding TextOnSearchQueryButton}" Command="{Binding CamlSearch}" Action="Meh"></Button>
<Button Name="btnCloseSearch" Content="{Binding TextOnCloseQueryButton}" Command="{Binding CloseSearch}" Action="NewMeh"></Button>
</Search>
</Tab>
</Tabs>`
So I read in the xml, and use methods like this to add buttons etc to the ribbon:
private void AddButtonToGroup(string header,RibbonGroup group,string command,string action)
{
RibbonButton button = new RibbonButton();
button.Label = header;
button.Name = action;
button.Click += new RoutedEventHandler(button_Click);
group.Items.Add(button);
}
with the following event handler:
void button_Click(object sender, RoutedEventArgs e)
{
Button clicked = (Button)sender;
MessageBox.Show("Button Clicked!");
MessageBox.Show("Performing Action:" + clicked.Name);
}.
The problem I have, is that this isn't the requirement- the event handler is hard coded. Is there any way to create event handlers dynamically? Can anyone point me in the right direction?
You could create a method that returns an Action<object, RoutedEventArgs>:
private Action<object, RoutedEventArgs> MakeButtonClickHandler()
{
return (object sender, RoutedEventArgs e) =>
{
// Put your code here, it will be called when
// the button is clicked
};
}
So MakeButtonClickHandler returns a new anonymous function each time it's called. Then assign it like this:
button.Click += new RoutedEventHandler(MakeButtonClickHandler());
Another way of accomplishing the same thing is to do this inline, like so:
button.Click += (object sender, RoutedEventArgs e) =>
{
// Put your code here
};
For some more information, take a look at Anonymous Functions on MSDN.
You need to define a set of actions that the user will be able to do. Then assign a name to each one of them and implement the actions in code.
For example, the XML would be:
<Button Content="TextOnAddQueryButton" Command="CamlAddQueryLine"></Button>
In code, you would assign the event handler like this:
private void AddButtonToGroup(string header,RibbonGroup group,string command,string action)
{
RibbonButton button = new RibbonButton();
button.Tag = command;
//...
}
And the handler for the button would be:
void button_Click(object sender, RoutedEventArgs e)
{
Button clicked = (Button)sender;
switch (clicked.Tag)
{
case "CamlAddQueryLine":
// Call action for CamlAddQueryLine
break;
}
}

Categories