Access a textbox declared in App.xaml (WpfNotifyIcon Tooltip) from another window - c#

I am using WpfNotifyIcon, I have declared it as a resource like this:
<Application x:Class="NotifyIconScratchPad2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="http://www.hardcodet.net/taskbar"
StartupUri="MainWindow.xaml">
<Application.Resources>
<tb:TaskbarIcon x:Key="MyNotifyIcon" IconSource="Icons/stopwatch_start.ico" ToolTipText="Hello world" >
<tb:TaskbarIcon.TrayToolTip>
<TextBlock x:Name="ChangeThis" Text="Hello world" />
</tb:TaskbarIcon.TrayToolTip>
</tb:TaskbarIcon>
</Application.Resources>
</Application>
To use this, I declare it in MainWindow.xaml.cs:
public TaskbarIcon tb;
public Window1 myWindow;
public MainWindow()
{
InitializeComponent();
tb = (TaskbarIcon) FindResource("MyNotifyIcon");
}
How can I access the textbox ChangeThis from another window?

The ultimate answer is setting up an event to do so.
The Model-View-View Model pattern is a good way to accomplish this.
Basically, you have a class that implements the INotifyPropertyChanged interface and a two-way data binding between both text boxes and the data source.

you can use FindName method:
TextBox txtToChange= tb.FindName("txt_ChangeThis") as TextBox;

Related

WPF binding from code-behind to Grid

I am trying to view the coordinates of a mouse click on a WPF screen with no luck. I have a rudimentary grid layout with a Textblock that will display these coordinates. I have bound values from the xaml to a code-behind before but am not sure if this direction is possible. My xaml is as follows
<Window x:Class="MouseUpExample.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"
Name="MyWindow">
<Grid>
<TextBlock Text="{Binding Path=GetMouseCoordinates}"/>
</Grid>
</Window>
and my code-behind is as follows
using System.Windows;
using System.Windows.Input;
using System.Windows.Shapes;
namespace MouseUpExample
{
public partial class MainWindow : Window
{
Point currentPoint = new Point();
public MainWindow()
{
InitializeComponent();
}
private string GetMouseCoordinates(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.ButtonState == MouseButtonState.Pressed)
{
currentPoint = e.GetPosition(this);
return currentPoint.ToString();
}
return "error";
}
}
any help would be greatly appreciated, thank you.
You've got multiple problems here.
Problem #1 is that you can't bind to a method. It needs to be a property and preferably either a DependencyProperty or one that takes part in the INotifyPropertyChanged interface.
Problem #2 is that binding works with the DataContext by default, but you're not setting the DataContext of your TextBlock, either explicitly or implicitly.
Problem #3 is that this doesn't really make sense. Is GetMouseCoordinates an event handler for something? You probably would want to split out the event handler and the property.
I'd suggest that you go read up on DataBinding in WPF and then give it another shot.
To be able to bind you'd have to set the DataContext to a class that implements INotifyPropertyChanged and bind to its properties. As you don't have it defined I'd set that text directly in the view.
<Window x:Class="MouseUpExample.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"
Name="MyWindow" MouseDown="MainWindow_OnMouseDown">
<Grid>
<TextBlock Name="MyTextBlock"/>
</Grid>
</Window>
And in the code behind:
private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e)
{
MyTextBlock.Text = e.GetPosition(this).ToString();
}

Triggering code-behind events with InputBindings

Got stuck while adding key-shortcuts to an MVVM app. Searched for a solution but could not find an example where triggering a command was hindered by the DataContext. This leads me to think I'm perhaps trying to do this the wrong way.
See the following example.
Window XAML
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="Stackoverflow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Window.InputBindings>
<KeyBinding Key="F" Modifiers="Control"/>
</Window.InputBindings>
<Grid>
<Button Content="Interface Action" Click="ButtonBase_OnClick"/>
</Grid>
</Window>
Window Code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
// Do Work
}
}
Trying to link the input binding to the button, what is the best design choice? These are the options I can think of:
Setup code-behind to be the Data-Context, create a code-behind property ViewModel and define the path deeper in the XAML tree
Write a key-press event in the code-behind and trigger the event from there
Both options seems to be either aesthetically wrong or cause a lot of extra code to be written. What would be the best option from an MVVM point of view?

Adding UserControl from one window to the MainWindow

I have a Mainwindow and a groupbox inside it.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.MainWindow"
Title="MainWindow"
Height="600" Width="800">
<Grid x:Name="MainGrid">
<GroupBox Header="Diagram Design" Name="gbDiagDesign">
</GroupBox>
</Grid>
</Window>
A simple UserControl
<UserControl x:Class="WpfApplication1.Controls.EntityControl"
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"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100">
<Grid>
<Button x:Name="btn_show" Content="show me" />
</Grid>
</UserControl>
and another window with an OK button in it,
the question is how do I add the UserControl to the Groupbox in the MainWindow after I press the OK button.
public partial class NewEntity
{
public NewEntity()
{
InitializeComponent();
}
private void OK_Click(object sender, RoutedEventArgs e)
{
EntityControl entcon = new EntityControl();
**MainWindow.gbDiagDesign.Children.Add(**
}
the last row gives me an error, "an object reference is required for the nonstatic field.."
You can access MainWindow using Application.Current.MainWindow but it will return instance of Window class. Typecasting is required to convert it to actual class instance i.e. MainWindow.
This should work:
((MainWindow)Application.Current.MainWindow).gbDiagDesign.Add(entcon);
WPF is meant to be programmed using the MVVM pattern. If you do it another way, you will have to fight WPF all along instead of using it's great power. If you insist on doing it the other way, at least make the compiler happy:
MainWindow is a instance variable of your application class. You are not inside your application class, so you need an instance of your application class first to access MainWindow. You will also need to cast it to your Window type.

Caliburn Micro and ModernUI Examples/Tutorials

does anyone have an example or tutorial on how to use Caliburn Micro together with ModernUi (https://mui.codeplex.com)?
Ok so I had a quick mess about with it and a look on the Mui forums and this seems to be the best approach:
Since the window loads content from URLs you need to take a view-first approach, and then locate the appropriate VM and bind the two.
The best way to do this appears to be via the ContentLoader class which is used to load the content into the ModernWindow when it is requested. You can just subclass DefaultContentLoader and provide the necessary CM magic to bind up loaded items:
public class ModernContentLoader : DefaultContentLoader
{
protected override object LoadContent(Uri uri)
{
var content = base.LoadContent(uri);
if (content == null)
return null;
// Locate the right viewmodel for this view
var vm = Caliburn.Micro.ViewModelLocator.LocateForView(content);
if (vm == null)
return content;
// Bind it up with CM magic
if (content is DependencyObject)
{
Caliburn.Micro.ViewModelBinder.Bind(vm, content as DependencyObject, null);
}
return content;
}
}
Your CM bootstrapper should just bootstrap a ModernWindow viewmodel which is backed by a ModernWindow based view (CM tries to use EnsureWindow which creates a new basic WPF Window class, unless of course your control already inherits from Window which ModernWindow does. If you need all dialogs and popups to be MUI you might need to reimplement WindowManager):
public class Bootstrapper : Bootstrapper<ModernWindowViewModel>
{
}
Which can be a conductor (OneActive) and looks like this:
public class ModernWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
}
And XAML for the view is
ModernWindowView.xaml
<mui:ModernWindow x:Class="WpfApplication4.ViewModels.ModernWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mui="http://firstfloorsoftware.com/ModernUI"
Title="ModernWindowView" Height="300" Width="300" ContentLoader="{StaticResource ModernContentLoader}">
<mui:ModernWindow.MenuLinkGroups>
<mui:LinkGroupCollection>
<mui:LinkGroup GroupName="Hello" DisplayName="Hello">
<mui:LinkGroup.Links>
<mui:Link Source="/ViewModels/ChildView.xaml" DisplayName="Click me"></mui:Link>
</mui:LinkGroup.Links>
</mui:LinkGroup>
</mui:LinkGroupCollection>
</mui:ModernWindow.MenuLinkGroups>
</mui:ModernWindow>
Obviously you need to make the loader a resource too:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Dark.xaml"/>
<ResourceDictionary>
<framework:ModernContentLoader x:Key="ModernContentLoader"></framework:ModernContentLoader>
<wpfApplication4:Bootstrapper x:Key="Bootstrapper"></wpfApplication4:Bootstrapper>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Here's the ChildViewModel I'm using as a test:
public class ChildViewModel : Conductor<IScreen>
{
public void ClickMe()
{
MessageBox.Show("Hello");
}
}
And the XAML for that (just a button)
<UserControl x:Class="WpfApplication4.ViewModels.ChildView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525">
<Grid>
<StackPanel>
<TextBlock >Hello World</TextBlock>
<Button x:Name="ClickMe" Width="140" Height="50">Hello World</Button>
</StackPanel>
</Grid>
</UserControl>
And the proof of concept:
I create a very, very simple sample of chat app using Modern UI for WPF, Caliburn Micro and MEF.
https://github.com/gblmarquez/mui-sample-chat
I hope it helps

UserControl calling methods and using variables outside it's class

I have a UserControl that looks like this:
<UserControl x:Class="Test3.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Button Name="mybutton" Content="Button Content"/>
</Grid>
</UserControl>
And a main window that uses it like so:
<Window Name="window_main" x:Class="Test3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test3">
<StackPanel>
<Label Name="mylabel" Content="Old Content"/>
<local:UserControl1/>
</StackPanel>
</Window>
What I want to happen, is for mybutton's click event handler to set the content of mylabel to "New Content". However, it appears that this is impossible. Is there in fact a way to do this?
I have chosen to answer this myself since my solution ended up being a bit more complete. I don't fully understand the "right" way to do this, but this is how I did it:
Window1 window_reference = (Window1)(Window1.GetWindow((Button)sender));
After this, the children (such as other xaml controls) of the main window can be seen at compile-time.
Additionally, a more direct way of doing this is to have a public member of the UserControl like so:
namespace UIDD_Test
{
public partial class UserControl1 : UserControl
{
public Window1 window_reference;
public UserControl1()
{
InitializeComponent();
}
}
}
Then whenever appropriate, you can set that member to reference whatever window you want. In my case I have a Window1 class which is derived from Window, so I can set that member of the UserControl1 class like so:
myusercontrol.window_reference = window_main;
Where I've set up the xaml like so:
<local:UserControl1 x:Name="myusercontrol"/>
And window_main is the Name of the main window (it's a Window1 class).
There are several solutions:
The quick and dirty: on mybutton's click event handler, find the parent Window using VisualTreeHelper, then do a ((Label) window.FindName("mylabel")).Content = "New Content".
The clean WPF way: create a new class, add a property object LabelContent and a property ICommand ChangeContentCommand, that will change LabelContent on execution. Set this class as the DataContext of the window, bind the Content of mylabel to LabelContent and the Command property of mybutton to ChangeContentCommand (the user control will inherit the data context).
The simplest way to do what you describe is to take advantage of event routing and just add a handler in the Window XAML:
<StackPanel ButtonBase.Click="Button_Click">
<Label Name="mylabel" Content="Old Content"/>
<local:UserControl1/>
</StackPanel>
and the handler method:
private void Button_Click(object sender, RoutedEventArgs e)
{
mylabel.Content = "New Content";
}
I suspect you probably have some more complications to this in your real application so you may need to do more to verify that the click is coming from the correct button by checking some properties on it.

Categories