How to Set Interaction Behavior from Code Behind in WPF? - c#

I have a behavior for Window as follows
<Window>
<my:Notify x:Name="Not"/>
<behaviors:Interaction.Behaviors>
<behavior:RebuildBehavior Element="{Binding ElementName=Not}" />
</behaviors:Interaction.Behaviors>
<Window>
now i want to write this code in code behind, so i used this code:
in Notify.cs (Loaded Event):
RebuildBehavior behavior = new RebuildBehavior();
behavior.Element = this;
Interaction.GetBehaviors(this).Add(behavior);
But my app crashes in the last line Interaction.GetBehaviors(this).Add(behavior);
System.Resources.MissingManifestResourceException: 'Could not find the
resource "ExceptionStringTable.resources" among the resources "
Did I write the correct code?
UPDATE:
I moved codes to window.cs (Loaded event)
RebuildBehavior behavior = new RebuildBehavior();
behavior.Element = Notify.Instance;
Interaction.GetBehaviors(this).Add(behavior);
crash fixed but not working

The following code would be the equivalent of your XAML markup:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
RebuildBehavior behavior = new RebuildBehavior();
BindingOperations.SetBinding(behavior, RebuildBehavior.ElementProperty,
new Binding() { ElementName ="Not" });
Interaction.GetBehaviors(this).Add(behavior);
}
}

Related

Minimising a WPF window in FsXaml

I am trying to convert this C# (WPF MVVM) code for minimising a WPF window into F#/FsXaml.
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = DependencyInjector.Retrieve<MainWindowViewModel>();
}
//... some generated code
private void OnKeyDownHandler(object sender, System.Windows.Input.KeyEventArgs e)
=> this.WindowState = (e.Key == Key.Escape) ? (WindowState)FormWindowState.Minimized : (WindowState)FormWindowState.Normal;
}
How to convert the C# code with OnKeyDownHandler into F#/FsXaml (only code behind, no MVVM)?
I did try to create the equivalent code in F#/FsXaml, but without success - see below.
open System.Windows.Input
type MainWindowXaml = FsXaml.XAML<"XAMLAndCodeBehind/MainWindow.xaml">
type MainWindow() as this =
inherit MainWindowXaml()
//... some code
let MyOnKeyDownHandler (e: System.Windows.Input.KeyEventArgs) =
match (e.Key = Key.Escape) with
| true -> this.WindowState <- Windows.WindowState.Minimized
| false -> this.WindowState <- Windows.WindowState.Normal
do
this.KeyDown.Add MyOnKeyDownHandler
The line with type MainWindow() gives these two errors:
No implementation was given for 'MainWindowXaml.OnKeyDownHandler(sender: obj, e: KeyEventArgs) : unit'
This type is 'abstract' since some abstract members have not been given an implementation. If this is intentional then add the '[]' attribute to your type.
Both relevant xaml files (in C# as well as in my F# attempt) contain KeyDown="OnKeyDownHandler" in the DataContext section.
EDIT: Added the relevant part of the XAML file:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Icon= ......
KeyDown="OnKeyDownHandler"
WindowStartupLocation="CenterScreen"
Title="...">
<Canvas Margin="0,0,2,2">
//... some XAML code...
</Canvas>
</Window>
I do not have direct experience with FsXaml, but the pattern you are using seems to be similar to the one in the WpfSimpleMvvmApplication sample. Here, the xaml file defines an event handler OnFullNameDoubleClick. This then becomes an abstract method in the generated class that needs to be overriden in the corresponding F# source file.
This means that your OnKeyDownHandler (which is presumably defined in the XAML file that is not included with your question) needs to be defined as an overriden method (and FsXaml automatically attaches the event handler):
open System.Windows.Input
type MainWindowXaml = FsXaml.XAML<"XAMLAndCodeBehind/MainWindow.xaml">
type MainWindow() =
inherit MainWindowXaml()
//... some code
override this.OnKeyDownHandler(_:obj, e: System.Windows.Input.KeyEventArgs) =
match (e.Key = Key.Escape) with
| true -> this.WindowState <- Windows.WindowState.Minimized
| false -> this.WindowState <- Windows.WindowState.Normal
EDIT: I also deleted as this from the class definition, because this was no longer needed - you can access this via the method definition (and this is a bit simpler).

XAML designer can't locate resource

I've custom control in shared project (resource dictionary in shared project).
Everything works fine in run time, xaml designer however throws exception:
Cannot locate resource 'mycontrol.xaml'.
The problem occurs when loading style for control:
public class MyControl: Control
{
public MyControl()
{
Resources = new ResourceDictionary() { Source = new Uri("pack://application:,,,/mycontrol.xaml") };
Style = (Style)Resources["somekey"];
}
}
Why does it works in run-time and doesn't during design time?
I can detect design time, but what to do then?
The WPF designer seems to have problem when loading xaml files from other projects. Could you try to load the xaml file using this annotation:
pack://application:,,,/PROJECTNAMESPACE;component/mycontrol.xaml
I would try
Uri res = new Uri("pack://siteoforigin:,,,/mycontrol.xaml", UriKind.Relative);
Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = res });

Why are my XAML controls null?

I have a XAML layout similar to this:
<Grid>
<TextBox x:Name="inputTextBox" LostFocus="inputTextBox_LostFocus" TextChanged="inputTextBox_TextChanged" GotFocus="inputTextBox_GotFocus" />
<ComboBox x:Name="inputComboBox" SelectionChanged="inputComboBox_SelectionChanged">
<ComboBoxItem IsSelected="True">10</ComboBoxItem>
<ComboBoxItem>15</ComboBoxItem>
<ComboBoxItem>20</ComboBoxItem>
</ComboBox>
<ComboBox x:Name="inputComboBoxTwo" SelectionChanged="inputComboBoxTwo_SelectionChanged">
<ComboBoxItem IsSelected="True">1</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
</ComboBox>
</Grid>
Pretty simple. In the codebehind C# file, I use these controls to take in a double from the TextBox, some more ints from the ComboBoxes, then I create a calculator type object with the data from the controls. I make the calculation and display the results in some other TextBlocks.
namespace TipCalc
{
public sealed partial class MainPage : Page
{
Calc x = new Calc();
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
//
//Appropriate event handlers from XAML controls that all call the calculation method.
//
private void calcIt()
{
x.amt = double.Parse(inputTextBox.Text);
x.cal1 = int.Parse(inputComboBox.SelectedItem.ToString());
x.cal2 = int.Parse(inputComboBoxTwo.SelectedItem.ToString());
//Send calculated values to output TextBlocks.
}
}
}
When I run this program, I hit a null pointer exception that is thrown when I attempt to access the text property of the TextBox. It turns out that all of the XAML controls are null. However, _contentLoaded is set to true and the code definition for this.IntializeComponent looks correct behind the scenes.
Why are all my controls set to null when it seems like everything is working correctly? Is there a way to manually initialize them if they aren't correctly being initialized automatically? Am I doing anything wrong?
the code run like:
Calc x = new Calc();
this.InitializeComponent();
Calc() was first than InitializeComponent(), but InitializeComponent() create your controls.
you can change to:
Calc x;
public MainPage()
{
this.InitializeComponent();
x = new Calc();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
I have the same problem with some of my TextBox controls when the class initializes. What I did to solve this is not the real and perfect solution because not all the controls (TextBox, ComboBox, RadioButton, etc) are null when the class is running, and there's something happening in my code or my app or my VS that I'm missing or doing wrong.... But at least is working fine now. I hope is useful to you:
if(TextBox1 == null)
{
//I'm re-initializing the control because is null
TextBox1 = new TextBox();
}
For your code it should be something like this:
if(inputTextBox == null)
{
inputTextBox.Text = new TextBox();
}
x.amt = double.Parse(inputTextBox.Text);
I hope this 'solution' is good enough for you. And for my poor English I apologize if I have mistakes, is not my native language.

WPF - Binding events to class methods of Item in ItemControl

I'm a bit new to WPF/XAML (though I've learnt C#) and would really appreciate any help for my question. I did look around other posts and google for a while but I can't seem to find a satisfactory or detailed answer to get me going on with my project. Please look below for details. Thanks you in advance!
Objective
I have a class called Tile that consists of a few properties and an event handler.
I also have an ItemControl that has a button (as by the DataTemplate), and whose ItemSource is a collection of Tiles.
Now, I want to bind the "Click" event of the Button so as to invoke the Event Handler method defined in the class Tile.
In other words when I click the button of any item in the ItemControl, the method handler of the corresponding Tile instance (from the collection) must be invoked. How would I tackle this problem?
Below is the entire code, simplified to avoid distraction:
XAML
<Window x:Class="SampleWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<!-- Make a ItemControl for "Tile"s. -->
<ItemsControl x:Name="TileList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Wire the click event of this Button
to event handler in the Tile class. -->
<Button Content="Show"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
CODE-BEHIND
namespace SampleWPF
{
public partial class MainWindow : Window
{
ObservableCollection<Tile> tiles;
public MainWindow()
{
InitializeComponent();
// Adding some sample data for testing.
tiles = new ObservableCollection<Tile>();
tiles.Add(new Tile("Item 1"));
tiles.Add(new Tile("Item 2"));
TileList.ItemsSource = tiles;
}
}
public class Tile : INotifyPropertyChanged
{
public string Data
{ /* Accessors and PropertyNotifiers */ }
public Tile(string data)
{ /* Initializing and assigning "Data" */ }
// INotifyPropertyChanged implementation...
// { ... }
// This event handler should be bound to the Button's "Click" event
// in the DataTemplate of the Item.
public void ShowButton_Click(object sender, EventArgs e)
{
MessageBox.Show("Viewing item from: " + this.Data);
}
}
}
Hence, if I click the first "Show" button, the output should be "Viewing item from: Item 1" and if I click the second "Show" Button, the output should be "Viewing item from: Item 2".
So what is the recommended/efficient way to do this? Is my code inappropriate for this requirement?
Event handlers are the wrong approach - use Commands and more importantly MVVM.
As I can see that you are new (and probably from a WinForms or ASP.NET background) you should read this blog to understand how your thinking needs to change - this is the most important part to understand before tackling WPF: http://rachel53461.wordpress.com/2012/10/12/switching-from-winforms-to-wpfmvvm/
You should also read Kent Boogart's blog on how MVVM works from base principles: http://kentb.blogspot.co.uk/2009/03/view-models-pocos-versus.html
Let me start with some basics:
Don't assign itemsource in codeBehind - use Binding like this:
<Controll ItemSource="{Binding MyObservableCollection}"/>
There are many ways You can achieve this. I think that using this.Data is not the best solution for this.
For example if Your tail have ID or something You can assign this id to button CommandParameter like below
<Button CommanParameter="{Binding Path=ID}" Click="ShowButton_Click"/>
And then in Your button_click event u can 'catch' this like this:
public void ShowButton_Click(object sender, EventArgs e)
{
int ID = int.Parse(((Button)sender).CommandParameter.ToString());
}
EDIT
To use this binding You need to set DataContext. You can do this in ctor like this:
public MainWindow()
{
InitializeComponent();
// Adding some sample data for testing.
tiles = new ObservableCollection<Tile>();
tiles.Add(new Tile("Item 1"));
tiles.Add(new Tile("Item 2"));
// below You are setting a datacontext of a MainWindow to itself
this.DataContext = this;
}
ANOTHER EDIT
Let's assume Your tail class have property called ID. If You bound this ID to Button.CommandParameter You can later retrieve the tile with linq like this:
public void ShowButton_click(object sender, EventArgs e)
{
int MyId = int.Parse(((Button)sender).CommandParameter.ToString());
Tile TileIWasSearchingFor = (from t in tiles where t.ID == MyId select t).First();
// do something with tile You found
}
Well since my requirement was rather "simple", I've managed a work around, avoiding commands. Thanks to the answer here by MajkeloDev: https://stackoverflow.com/a/27419974/3998255 for guidance.
This is the final event handler:
public void ShowButton_Click(object sender, EventArgs e)
{
Tile requestingTile = (sender as Button).DataContext as Tile;
if(requestingTile != null)
MessageBox.Show("Viewing item from: " + this.Data);
// Or whatever else you want to do with the object...
}
Also, adding the ItemSource as a XAML attribute:
<ItemsControl x:Name="TileList" ItemsSource="{Binding tiles}">
And setting DataContext in constructor of MainWindow:
public MainWindow()
{
this.DataContext = this;
// Whatever else you want to do...
}
Well it works as required.

Opening new a WPF Window

I'm currently trying to have a pop-out button, such that when it's clicked the current grid will appear in a new window populated with the exact same information.
I got the new Window to appear but I'm trying to have the bindings set but unsure how to do that. If I can get some help please. When I execute OpenChildWindow it opens but nothing populates.
Viewmodel:
public ObservableCollection<PaymentInfo> AmortizationCollection {get; set;}
public void OpenChildWindow()
{
new ScheduleView().Show();
}
LoanView.xaml and ScheduleView.xaml
<telerik:RadGridView Grid.Row="3" Grid.ColumnSpan="3" x:Name="AmortGrid"
ScrollViewer.CanContentScroll ="True"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
Height="650" AutoGenerateColumns="True" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ShowGroupPanel="False"
ItemsSource="{Binding AmortizationCollection, Mode=TwoWay}">
My attempt at Content Setting
Scheduleview.xaml.cs
public ObservableCollection<PaymentInfo> AmortizationCollection { get; set; }
public ScheduleView()
{
InitializeComponent();
AmortGrid.ItemsSource = Content;
}
Viewmodel:
public void OpenChildWindow()
{
ScheduleView _newScheduleView = new ScheduleView();
_newScheduleView.Content = AmortizationCollection;
_newScheduleView.Show();
}
The window just appears (Collection) no datagrid or anyhting.
When using WPF, we tend to use DataTemplates to define what our data objects look like in the UI, so to recreate some UI control, we just need to pass the data object and ensure that the DataTemplate is accessible from both locations. You show code from your 'view model', but view models shouldn't know anything about the views and certainly don't open new windows like that.
However, incorrect terminology aside, your simplest solution would be to simply pass the collection to the new Window in the constructor:
public void OpenChildWindow()
{
ScheduleView _newScheduleView = new ScheduleView(AmortizationCollection);
_newScheduleView.Show();
}
Then in ScheduleView.xaml.cs:
public ScheduleView(ObservableCollection<PaymentInfo> amortizationCollection)
{
InitializeComponent();
AmortizationCollection = amortizationCollection;
AmortGrid.ItemsSource = AmortizationCollection;
}
As long as you have the same XAML in each place, it should look the same.
If that is a Window then you can try to set some Content to it, this way the new control that you'll pass will be set as the content of the new window. I think this property is missing in your code untill now.
// create instance
ScheduleView wind = new ScheduleView();
// set the content, can be a window or a page or anything
wind.Content = new SomeControl;
// show it.
wind.Show();
http://msdn.microsoft.com/en-us/library/system.windows.window.content(v=vs.95).aspx
A previous thread from Stack Overflow has this, but in a manner that was too general.
Changing content of Window (WPF)

Categories

Resources