Minimising a WPF window in FsXaml - c#

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).

Related

How to Set Interaction Behavior from Code Behind in WPF?

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);
}
}

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.

InvalidOperationException, the calling thread should be STA because

I am developping a project in order to manipulate a lot of objects with several modalities (mouse, leapmotion, touch ...). I made it using the MVVM pattern soI have several Views and ViewModels for all the components I will use. To make it easier to develop I chose to have a Canvas component in which I will manipulate Grids. Each Grid can contain any type of object (Shape, Text, Image, Documents...).
To be able to have all modalities linked to my method, I decided to build one listener per modality (1 for the mouse, 1 for the leapmotion...) and make them detect basic gestures (as Click, DoubleClick ...). All the gestures I chose to detect are associate with a method via a Dictionary. Anyway the linking is working as expected as it executes the right method. T o give an example I have the action calling in my mouse listener:
if (_leftClickCounter == 1 && _capturedLeft == false)
{
if (_dic.ContainsKey(Key.OnClick))
{
Action<object> action = _dic[Key.OnClick];
action.Invoke(null);
}
}
Where:
_dic is my dictionary
Key an enumeration of gestures (as OnClick, OnDoubleClick ...)
action the method to execute
In my example the method executed is:
public void Add(object sender)
{
ObjectModel objectModel = new ObjectModel();
ObjectView objectView = new ObjectView(objectModel);
this.objectViews.Add(objectView);
}
Where sender is just used for test purpose. It remains unused in the method. My execution stops when it tries to instanciate my ObjectView saying:
InvalidOperationException
The calling thread must be STA, because many UI components require this
My ObjectView.xaml.cs class is:
public partial class ObjectView : UserControl
{
public ObjectView(ObjectModel obj)
{
InitializeComponent();
EventLinker linker = new EventLinker(this.visualBox);
ObjectViewModel objectVM = new ObjectViewModel(obj, linker);
this.DataContext = objectVM;
}
}
And its ObjectView.xaml defining the UserControl to use is very basic:
<UserControl x:Class="AusyTouchMultimodal_v1.View.ObjectView"
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="300" d:DesignWidth="300">
<Grid x:Name="visualBox" Background="Blue"/>
</UserControl>
I dont have any compilation errors, just this InvalidOperationException. Can someone explain this issue to me?
Thanks!
Try calling your actions in ui thread, like this
if (_leftClickCounter == 1 && _capturedLeft == false)
{
if (_dic.ContainsKey(Key.OnClick))
{
Action<object> action = _dic[Key.OnClick];
// action.Invoke(null);
System.Windows.Application.Current.Dispatcher.BeginInvoke( call your action )
}
}

Filtering CollectionViewSource

I want to make a ComboBox bound to my data, with a filter. For that I've created a TextBox and a ComboBox. In the code behind I read a file and generate objects of class Channel that are stored as items of the ComboBox. Although the compiler throws no error the filtering doesn't work properly. If I write something the data is gone, if I erase, it's back. After trying and trying I've realized that if I started typing "myNamespace.myChannel" (Unico.Canal) the data remained, but don't filter. Strange behaviour, indeed. I suspect that I've put something in wrong place.
(for better understanding I've translated the code, Canal=Channel)
Here is the scheme of my code:
namespace Unico
{
public partial class ControlesArchivo : UserControl, INotifyPropertyChanged
{
public ControlesArchivo()
{
InitializeComponent();
}
public ObservableCollection<Channel> myListChannels //with INotifyPropertyChanged implemented. But I think I don't need it.
private void loadButton_Click(object sender, RoutedEventArgs e)
{
File loadedFile = new File();
loadedFile.read(); //Generates a bunch of data in lists.
foreach (Channel mychan in loadedFile.channels) //Just duplicating the data (maybe this can be avoided)
{
myListChannels.Add(mychan);
}
var view = CollectionViewSource.GetDefaultView(this.miListaDeCanales);
view.Filter = delegate(object o)
{
if (o.ToString().Contains(myTextBox.Text)) //Delicate place
{
return true;
}
return false;
};
myComboBox.ItemsSource = view;
DataContext = this;
}
private void myTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
((ICollectionView)myComboBox.ItemsSource).Refresh();
myComboBox.SelectedIndex = 0;
}
}
}
The data is bound in XAML with:
ItemsSource="{Binding view}"
EDIT: I think I know where is the problem: I'm not specifing the property to filter. I mean, what you see in the ComboBox is the property channelName of the class Channel listed in myListChannels. When I'm setting the filter, shouldn't I let know what I'm filtering? How could I write this? Thank you very much.
Yes your assumption is correct.
I'm assuming with your translations,
public ObservableCollection<Channel> myListChannels;
is actually
public ObservableCollection<Canal> miListaDeCanales;
with the class Canal in the namespace Unico
Update:
In your filter try using the property that is rendered in the ComboBox than use the ToString() on the object(o) if you've not overridden ToString() from System.Object.
try switching
if (o.ToString().Contains(myTextBox.Text))
to
if (((Canal)o).NameProperty.Contains(myTextBox.Text))
^^ that should fix your issue.
Do you have a DataTemplate for ComboBox.ItemTemplate in xaml. That will explain why you see the valid value rendered in the ComboBox, else all the ComboBoxItem's will also render as Unico.Canal

Categories

Resources