WPF button click and command doesn't work together MVVM - c#

I'm developing some wpf application, using mvvm. I'm trying to use button click event and command together but command never get executed. Also when I use only command without click event it works perfect. Here is the code:
<ControlTemplate x:Key="reminderDataTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="150" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="New reminder:" HorizontalAlignment="Left" />
<TextBox Text="{Binding Path=ReminderText, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Height="150" Width="200" HorizontalAlignment="Left"/>
<Button Name="btnSaveReminder" Grid.Row="2" Content="Save" Width="auto" Height="20" HorizontalAlignment="Left" Click="btnSaveReminder_Click" Command="{Binding Path= btnSaveReminder}" />
</Grid>
</ControlTemplate>
Why is this happening?
Just to mention that I must use click and command together becouse my view and viewmodel are in different projects.
UPDATE:
Also to say that, when I use click and command together in buttons outside of control template, everything works perfect.

on the Click event, execute command.
private void btnClick(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
btn.Command.Execute(btn.CommandParameter);
}

Related

Hiding/Showing a UserControl WPF

I am building a WPF MVVM application.
What I have:
I have a ShellWindow which looks like this:
It is composed by 2 rows:
1: the hamburger menu (not important) with Height="*"
2: the console with Height="100"
The console is a UserControl:
<UserControl
//namespaces>
<Grid Name="LoggingGrid" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Margin="{StaticResource SmallLeftMargin}">
<Button
x:Name="CollapseBtn"
Width="25"
Height="25"
Click="CollapseBtn_Click"
Content="▲">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse Fill="White" />
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
<StackPanel Margin="5,0,0,0" Orientation="Horizontal">
<Image
Height="25"
Source="/Images/console-icon.png"
Visibility="Visible" />
<Label
Content="Console"
FontSize="16"
Foreground="White" />
</StackPanel>
</TextBlock>
<Border Grid.Row="1">
<ListView
x:Name="LoggingList"
Margin="5"
Background="Black"
BorderThickness="0"
Foreground="White"
ItemsSource="{Binding Logs, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Border>
</Grid>
</UserControl>
I have omitted the non-important things.
What I want to do:
Whenever the user clicks on the button, the console should collapse and look something like this:
The arrow is also changed.
How can I implement this? What is the best approach using MVVM?
What I have tried:
I have tried using a button click event handler in the code behind - CollapseBtn_Click, just to see what will happen:
private void CollapseBtn_Click(object sender, System.Windows.RoutedEventArgs e)
{
LoggingGrid.Visibility = System.Windows.Visibility.Hidden;
}
Apparently it removes the user control and leaves a white background where it used to be.
Instead of setting the Visibility of the whole LoggingGrid to Hidden, you should set the Visibility of the LoggingList to Collapsed. (For the difference between Hidden and Collapsed, see here: Difference between Visibility.Collapsed and Visibility.Hidden).
Depending on your layout in the ShellWindow you probably have to adjust your row height configuration in the UserControl such that the collapsed LoggingGrid leads to a row with a height of zero.
Regarding MVVM the best approach would be to bind the Button to a bool property ConsoleVisible on your ViewModel such that clicking the button toggles the property between true and false. The styling of the button can be bound to the same property. For the LoggingList Visibility you could use a Binding with a BooleanToVisibilityConverter on the same property.

Detect click outside control

I display TestControl in grid when user clicks on the button:
<Grid>
<myControls:TestControl Panel.ZIndex="2" Visibility="Collapsed" Width="100" Height="100" />
<Button "Open Test Control" Click="btnOpen_Click" />
</Grid>
How can I hide TestControl when user clicks outside this control?
You could handle the PreviewMouseLeftButtonDown event for the Grid:
<Grid Background="Transparent" PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<myControls:TestControl x:Name="tc" Visibility="Collapsed" Width="100" Height="100" />
<Button Content="Open Test Control" Click="btnOpen_Click" Grid.Row="1" />
</Grid>
private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!tc.IsMouseOver)
tc.Visibility = Visibility.Collapsed;
}
Haven't tried it, but LostFocus event may be a way to go, given that you set focus to your test control after showing it.

Two AutoCompleteBox controls in two different tabs in wpf

I have used two AutoCompleteBox controls in two different tabs in a wpf window.
Control in first tab is working fine. First Control
But the control in second tab, data is binding and I could see the matched strings in the dropdown list.
I couldn't select the items from the list using mouse or arrow keys. Second Control
When I moved the second control to new window, it is working fine.
I couldn't understand what is the actual issue?
Please find below code:
Autocompletebox in first tab
<ctrls:AutoCompleteBox Grid.Column="1" x:Name="txtFirst" VerticalAlignment="Center" Margin="0,0,0,10" />
Autocompletebox in Second tab
<ctrls:AutoCompleteBox Grid.Column="1" x:Name="txtSecond" VerticalAlignment="Center" Margin="0,0,0,10" />
Xaml code for Tab control
<TabControl Grid.Row="1"
x:Name="tabCtrl"
SelectionChanged="tabCtrl_SelectionChanged">
<TabItem x:Name="tab1"
Header="First">
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="500" />
</Grid.ColumnDefinitions>
<TextBlock Text="First"
VerticalAlignment="Center"
Margin="0,0,0,10" />
<ctrls:AutoCompleteBox Grid.Column="1"
x:Name="txtFirst"
VerticalAlignment="Center"
Margin="0,0,0,10" />
</Grid>
</ScrollViewer>
</TabItem>
<TabItem x:Name="tab2"
Header="Second">
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="500" />
</Grid.ColumnDefinitions>
<TextBlock Text="Second"
VerticalAlignment="Center"
Margin="0,0,0,10" />
<ctrls:AutoCompleteBox Grid.Column="1"
x:Name="txtSecond"
VerticalAlignment="Center"
Margin="0,0,0,10" />
</Grid>
</ScrollViewer>
</TabItem>
</TabControl>
And the code behind
var data = db.tblname.Select(c => c.propertyname).ToList();
txtFirst.ItemsSource = data;
var data1 = db.tblname.Select(c => c.propertyname).ToList();
txtSecond.ItemsSource = data1;
Your C# code is fine.
You should take a look at the XAML.
(Provide XAML to us as well.)
After thorough debugging of my code, I figured out that the issue was due to SelectionChanged event of TabControl.
Whenever I select an item from the Autocompletebox control, SelectionChanged event of TabControl was getting fired, which led to chaos, as my binding logic for Autocompletebox was in SelectionChanged event.
Still I am not getting why do my Autocompletebox control triggers SelectionChanged event of TabControl without the registeration of SelectionChanged event for Autocompletebox Control.
But Below code overcame the issue
private void tabCtrl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.Source is TabControl)
{
// Business logic for binding autocompletebox
}
}
Thanks everyone for their support!

wpf user control - how to set text from outer uc

i have a problem , i have 2 uc that displayed in main windows.
i want when i press on button in the first uc the text in the other uc will change
this the axml i use and the code
first uc
<Grid.RowDefinitions>
<RowDefinition ></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Width="40" Height="40" Name="playbtn" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0" Click="playbtn_Click" >
<Button.Content>
<Image Source="/img/player_play.png" ></Image>
</Button.Content>
</Button>
<Button Width="40" Height="40" Name="pausebtn" Grid.Column="1" Grid.Row="0" >
<Button.Content>
<Image Source="/img/player_pause.png" ></Image>
</Button.Content>
</Button>
<Button Width="40" Height="40" Name="stopbtn" Grid.Column="2" Grid.Row="0" >
<Button.Content>
<Image Source="/img/player_stop.png" ></Image>
</Button.Content>
</Button>
</Grid>
</StackPanel>
</Grid>
</StackPanel>
secound uc <TextBlock Name="Progresstimertext" Text="00:00:00" FontSize="20"></TextBlock>
so i want to press on start button and the timer will be change? how to
10x
This should be managed in the View Model of your Main Window, as this seems the only location that has knowledge of both controls and it sounds as though each of your user control's shouldn't necessarily have knowledge of each other.
Ideally, you should have a reference to each of your user controls in the code-behind (or view model class is you have a dedicated one).
If in code behind, you would simply expose these by giving them names.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="your controls reference here">
<Grid>
<Grid.ColumnDefinitions>
</ColumnDefinition>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:UserControl1 x:Name="Control1"/>
<controls:UserControl2 x:Name="Control2" Grid.Column="1"/>
</Grid>
</Window>
Now you can reference them in your code behind by name Control1 and Control2. These user control classes should be exposing the properties that you want to control, in your case ProgressTimerText, such that, in the code-behind, you can set it easily such as
Control2.ProgressTimerText = "00:00:00";
Where to do this? Well you probably want to create a Stopped event on your Control1, that you can attach to your code behind - in your case, something like Stopped. In your UserControl1 class you should declare something like
public event EventHandler Stopped;
And then in the local event handler for the Click of the stop button, invoke that event.
private void Stop_Click(object sender, RoutedEventArgs e)
{
if (Stopped != null)
Stopped(this, e);
}
Attach that in your MainWindow:
<controls:UserControl1 x:Name="Control1" Stopped=Control1_Stopped/>
Now in your code-behind you should have something like:
private void Control1_Stopped(object sender, RoutedEventArgs e)
{
Control2.ProgressTimerText = "00:00:00";
}

Binding Button to its handler by convention doesn't work

I have a WPF application with caliburn.micro. There is a user control MyView in a tab item of a tab control. Within that user control, there is another tab control. In one of its tabs, I added a button, and a corresponding method with the same name in the MyViewModel. But this method is not called when I click the button. Could you please tell what might cause it?
Thanks.
In the View:
<TabControl SelectedIndex="{Binding SelectedTabIndex}">
...
<TabItem x:Name="TextTab" Header="Text">
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
...
</ScrollViewer>
<Button Grid.Row="1" x:Name="SaveText" Content="Save" Width="50" Height="25" />
</Grid>
</TabItem>
In the ViewModel:
public void SaveText()
{
...
}
I found a solution:
<Button Grid.Row="1" x:Name="SaveText" cal:Message.Attach="SaveText" Content="Save" Width="50" Height="25" />
Still don't know why the convention didn't work without "Attach".

Categories

Resources