I've a UserControl which is the content of a ContentControl. In the UserControl is a Button, which Command property is bound to my VM. The DataContext is set in the ContentControl. I got not binding errors.
Unfortunately the command won't execute by clicking on the Button. When I write a unclean workaround in code-behind it would work.
Maybe have anyone a idea how to fix it. Thanks!
UserControl XAML:
<!-- Button in UserControl -->
<Button Content="PrismCommand" Command="{Binding PrismCmd}" PreviewMouseDown="Button_PreviewMouseDown"/>
UserControl code-behind:
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//this would fire the command
Button bt = sender as Button;
t.Command.Execute(null);
}
VM:
public DelegateCommand PrismCmd { get; private set; }
public MainViewModel()
{
PrismCmd = new DelegateCommand(PrismTest);
}
private void PrismTest()
{
MessageBox.Show("Execute");
}
Related
I am creating a wpf application and in that application there is one window with a grid in it, and in this grid I host my user controls by adding them to the grid,
I have to explain this in detail to show it’s working
so basically there is a side menu, which In actuality is a listview, and when a listview item is selected or that event is raised, the corresponding user control to that listview item is added to the grid
Well this worked, but this was slow, so instead what I did was load all the user controls to the grid and controlled the visibility property of the user control corresponding to its listview item selected
This is the code that I am using
//Adds usercontrol to grid
private void Window_Loaded(object sender, RoutedEventArgs e)
{
UserControl usc = null;
usc = new Home();
usc.Tag = "Home";
LoadGrid.Children.Add(usc)
ShowUserControl("Home");
}
//controls UserControl visibility
private void ShowUserContro(string v)
{
foreach (UIElement item in LoadGrid.Children)
{
if (item is UserControl)
{
UserControl x = (UserControl)item;
if (x.Tag != null)
{
if (x.Tag.ToString().ToUpper() == v.ToUpper())
{
x.Visibility = Visibility.Visible;
}
else
{
x.Visibility = Visibility.Collapsed;
}
}
}
}
}
//controls listview selection changed event
private void ListViewMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ShowUserContro((((ListViewItem)((ListView)sender).SelectedItem).Name));
}
My question is I want the grid to host a user control but it should be tied to a button inside the user control
Example :
(The main window where the grid is present, children's are added to the grid, and its visibility property which is collapsed and visible, this event is tied up to the listview menu)
when lets say a listview item called home is selected, the user control corrosponding to home is added and made visible, this has a button it is supposed to show another user control (that hosts a few textbox and data-grid)
You can send the main window as an input parameter to the
user-controller Constructors and in the main window put the method
of adding the user-controller and add or manage the user-controller
there.
The next solution is to give an event to the button inside the
current control and use that event in the main window, and of course
this method is better.
MainWindow.cs
public MainWindow()
{
InitializeComponent();
MyUserControl myControl = new MyUserControl();
myControl.ButtonClicked += MyControl_ButtonClicked;
this.stMain.Children.Add(myControl);
}
private void MyControl_ButtonClicked(object Sender)
{
//add userControl
MessageBox.Show("Test");
}
MyUserControl.Xaml
<UserControl x:Class="TestProj.MyUserControl"
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"
xmlns:local="clr-namespace:TestProj"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button x:Name="btnAddOtheruserControl" Click="btnAddOtheruserControl_Click" Content="add Another UserControl"> </Button>
</Grid>
</UserControl>
MyUserControl.cs
public partial class MyUserControl : UserControl
{
public event EventAddAnotherUserControl ButtonClicked;
public delegate void EventAddAnotherUserControl(object Sender);
public MyUserControl()
{
InitializeComponent();
}
private void btnAddOtheruserControl_Click(object sender, RoutedEventArgs e)
{
if (ButtonClicked != null)
ButtonClicked(btnAddOtheruserControl);
}
}
Im trying to get the previous selected tabs content when it is changed to another in a TabControl. For this i subscribe to the SelectionChanged event like so:
tabControl.SelectionChanged += getPreviousData
Then the getPreviousData method looks like this:
private void getPreviousData(object sender, SelectionChangedEventArgs e)
{
e.RemovedItems[0].something
}
Im a little unsure as to how i grab the previous tab content. The previous tab has a textbox control that i need to get the name of, when i change the tab. How can i accomplish that?
Assuming you have a XAML like that
<TabControl x:Name="tabControl" SelectionChanged="tabControl_SelectionChanged">
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<TextBox Width="100" Height="23"></TextBox>
</Grid>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<TextBlock x:Name="TextBlock"></TextBlock>
</Grid>
</TabItem>
</TabControl>
First option
Then you can access children of removed TabItem using this code
private void tabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count != 0)
{
var tabItem = (TabItem)e.RemovedItems[0];
var content = (Grid)tabItem.Content;
var textBox = content.Children.OfType<TextBox>().First();
var text = textBox.Text;
}
}
Second option
You can name your textbox
<TextBox x:Name="TextBoxInFirstTab" Width="100" Height="23"></TextBox>
And access it using his name
var text2 = TextBoxInFirstTab.Text;
Third option
Use MVVM, check this answer MVVM: Tutorial from start to finish?
I am going to provide a simple sample, without any framework, but I suggest you to use anyone, like MVVM Light ToolKit.
Create a View Model
Implement the INotifyPropertyChanged interface
Create a property that will hold your text value, and in the set call the OnPropertyChanged
public class MyViewModel : INotifyPropertyChanged
{
private string _textInFirstTab;
public string TextInFirstTab
{
get { return _textInFirstTab; }
set
{
_textInFirstTab = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then in your Window constructor, set the DataContext property from Window, to a new instance for your MyViewModel.
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
Then in your XAML set the Text attribute with a Binding expression
<TextBox x:Name="TextBox" Width="100" Height="23" Text="{Binding TextInFirstTab}"></TextBox>
And in your tabControl_SelectionChanged event, you can access the value like that:
private void tabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count != 0)
{
var myViewModel = (MyViewModel)DataContext;
var text = myViewModel.TextInFirstTab;
}
}
If it is switching between existing tabs which you are after, then I would suggest simply storing the index of the selected tab in a class variable.
Sample code looks like this:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
// variable to store index of tab which was most recently selected
private int lastSelectedTabIndex = -1;
public Form1()
{
InitializeComponent();
// initialise the last selected index
lastSelectedTabIndex = tabControl1.SelectedIndex;
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
// sanity check: if something went wrong, don't try and display non-existent tab data
if (lastSelectedTabIndex > -1)
{
MessageBox.Show(string.Format("Previous tab: {0} - {1}", lastSelectedTabIndex, tabControl1.TabPages[lastSelectedTabIndex].Text));
}
// store this tab as the one which was most recently selected
lastSelectedTabIndex = tabControl1.SelectedIndex;
}
}
}
This was written and tested in a simple application with one form and a TabControl added. No changes were made to the default properties.
You will, of course, have to hook into the event handler. I did so by double-clicking it in the IDE, but you could also hook in manually by adding:
this.tabControl1.SelectedIndexChanged += new System.EventHandler(this.tabControl1_SelectedIndexChanged);
to the form constructor, called "Form1()" in the example code.
Getting the name of a textbox is an unusual thing to want to do. May I ask what you are trying to achieve? There's probably a better way to do it than trying to determine the name of a control.
I have added a click event on the calendar control. But with my implementation, this event don't work.
My code in Cal.cs control:
#region click
public static RoutedEvent ClickEvent =
EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Cal));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
protected virtual void OnClick()
{
RoutedEventArgs args = new RoutedEventArgs(ClickEvent, this);
RaiseEvent(args);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
OnClick();
}
#endregion
XAML code :
<Calen:Cal x:Name="Calendar" Margin="0,50,0,0" Click="Calendar_Click"/>
C# code :
private void Calendar_Click(object sender, RoutedEventArgs e)
{
string t = "";
}
I don't found any solution. I don't know why this code don't work correctly.
Can you help me with this problem please ?
You need to set the DataContext to point to the class that contains the code behind.
<UserControl>
DataContext="{Binding RelativeSource={RelativeSource Self}}">
</UserControl>
This is necessary because unfortunately, by default, the DataContext is not set up correctly in WPF.
For more info on DataContext, see ReSharper WPF error: "Cannot resolve symbol "MyVariable" due to unknown DataContext".
I've two windows: Main Window, Log Window. How can I update the listbox in the Log Window when some action is happened in the Main Window (e.g. button is clicked)?
Below is the code for listbox in Log Window:
<ListBox x:Name="DebugLogLb" BorderBrush="{x:Null}">
<TextBlock x:Name="DebugLogTb" Text="{Binding LogText}" Background="{x:Null}" />
</ListBox>
When the button in the Main Window is clicked, it will update the listbox. I tried with the code below but it doesn't work.
private void Btn1_Click(object sender, RoutedEventArgs e)
{
var log = new LogWindow();
log.DebugLogLb.Items.Add(new { LogText = "Button 1 is clicked" });
}
I'm able to update the listbox if I put everything in the same window, but I failed to do so with two windows.
My expected output would be like:
Even if both windows are opened, when the buttons in the Main Window are clicked, it will directly update in the Log Window as well.
Thanks for any helps in advanced.
It's hard to tell where you are going wrong without seeing more of the code. This is an example that works. It creates a new LogWindow in the MainWindow ctor and sets the DataContext. When the button is clicked the handler calls show on the window. The ListBox's itemssource property is bound to an ObservableCollection of strings. So any adds/removes are automatically updated on the UI.
LogWindows xaml
<Window x:Class="WpfApplication7.LogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LogWindow" Height="300" Width="300">
<Grid>
<ListBox x:Name="DebugLogLb" BorderBrush="{x:Null}" ItemsSource="{Binding LogText}" />
</Grid>
MainWindow code-behind
public partial class MainWindow : Window
{
LogWindow _logWindow;
public MainWindow()
{
InitializeComponent();
LogText = new ObservableCollection<string>();
_logWindow = new LogWindow();
_logWindow.DataContext = this;
_logWindow.Closed += _logWindow_Closed;
}
private void _logWindow_Closed(object sender, EventArgs e)
{
_logWindow = new LogWindow();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_logWindow.Show();
LogText.Add("Button1 Clicked");
}
public ObservableCollection<string> LogText { get; set; }
}
I must confess I have problems understanding the way wpf works. I have a usercontrol BottomControl nested in my Mainwindow. If a Button in BottomControl is clicked I want certain changes in my Mainwindow (changing content of a textbox for example) to occur.
The easy thing to do is obviously to just call a public procedure in the Click_Event but this is not quite elegant. I got so far as to use RoutedCommands.
public static readonly RoutedCommand BottomGridReSize = new RoutedCommand();
In XAML of Usercontrol
<Button Style="{StaticResource TransparentButton}" Command="{x:Static local:Commands.BottomGridReSize}" >
In Code of MainWindow
void BottomGridReSize_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
void BottomGridReSize_Executed(object sender, ExecutedRoutedEventArgs e)
{
\\Do some stuff
}
Obviously Mainwindow can't use these events because ist doesn't recognize them. What am I missing?
Any help would very much be appreciated
Jon
just for my understanding: you have a BottomControl with a Button and you when the Button is clicked there should something happen in your mainwindow. so why not simply create a DependencyProperty of type ICommand in your BottomControl and bind this to the Button. if you do this you can simply bind a Command of your MainViewmodel to this DP.
<uc:BottomControl MyDPCommand="{Binding MyMainViewmodelCommand}" />