Bind to result of static class' method - wpf

I have the following:
namespace Foo {
public static class Bar {
public static int Fubar() {
return 100;
}
}
}
Now I'm in xaml. I want to use that method to set the height of my rectangle.
<Rectangle Height="{Binding Source=???}">

You need an ObjectDataProvider to bind to a method.
Example (adjust to your NameSpace / Class / Method):
<Window x:Class="SerialPortBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ports="clr-namespace:System.IO.Ports;assembly=System"
Title="MainWindow" SizeToContent="WidthAndHeight">
<Window.Resources>
<ObjectDataProvider ObjectType="{x:Type ports:SerialPort}"
MethodName="GetPortNames" x:Key="portNames"/>
</Window.Resources>
<ComboBox ItemsSource="{Binding Source={StaticResource portNames}}"/>
</Window>
The assembly portion of the xmnls at the top may not be needed; ignore it if it does not come up in CodeSense.

Try something like this:-
{Binding Source={x:Static classnamespace:Bar}, Path=Fubar}

Related

Binding to a list of FontWeight and FontStyle values

I am trying to bind to the list of defined FontWeights and FontStyles in WPF.
I was hoping to use the same technique used in this thread about Displaying the list of system fonts; however, I cannot documentation on the get_systemFontFaimlies method for Fonts structure and likewise I cannot find documentation for the FontWeights and FontStyles either.
Thank you for your help.
Edit:
While the response that #ethicallogics provided was helpful, and it is probably more flexible than my solution, I just simply declared arrays in my XAML and chose which FontWeight and FontStyle options I wanted to provide to the end user.
I also found documentation that I couldn't find yesterday for the SystemFonts.get_systemFontFaimlies method here. It is a shame but it appears that the FontWeights and FontStyles structures do not have anything like the get method that the SystemFonts structure has which is why I had to explicitly define arrays with the items that I wanted to provide to the end user.
Here is my XAML for the arrays I defined:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
xmlns:win="clr-namespace:System.Windows;assembly=PresentationCore"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ObjectDataProvider x:Key="FontFamilyOptions" ObjectType="{x:Type media:Fonts}" MethodName="get_SystemFontFamilies"/>
<x:Array x:Key="FontWeightOptions" Type="win:FontWeight">
<win:FontWeight>Normal</win:FontWeight>
<win:FontWeight>Bold</win:FontWeight>
<win:FontWeight>ExtraBold</win:FontWeight>
</x:Array>
<x:Array x:Key="FontStyleOptions" Type="win:FontStyle">
<win:FontStyle>Normal</win:FontStyle>
<win:FontStyle>Italic</win:FontStyle>
<win:FontStyle>Oblique</win:FontStyle>
</x:Array>
</ResourceDictionary>
Create a FontWrapper class like
public class FontsWrapper
{
static ICollection<FontWeight> fontWeights;
static ICollection<FontStyle> fontStyles;
static ICollection<FontFamily> fontFamilies;
public static ICollection<FontStyle> GetFontStyles()
{
return fontStyles ?? (fontStyles = new List<FontStyle>() { System.Windows.FontStyles.Italic, System.Windows.FontStyles.Normal, System.Windows.FontStyles.Oblique });//TODO:Get by reflection
}
public static ICollection<FontFamily> GetFontFamilies()
{
return fontFamilies ?? (fontFamilies = Fonts.SystemFontFamilies);
}
public static ICollection<FontWeight> GetFontWeights()
{
if (fontWeights == null)
fontWeights = new List<FontWeight>();
else
return fontWeights;
var type = typeof(FontWeights);
foreach (var p in type.GetProperties().Where(s => s.PropertyType == typeof(FontWeight)))
{
fontWeights.Add((FontWeight)p.GetValue(null, null));
}
return fontWeights;
}
public static ICollection<FontWeight> FontWeights
{
get { return fontWeights ?? (fontWeights = GetFontWeights()); }
}
public static ICollection<FontStyle> FontStyles
{
get { return fontStyles ?? (fontStyles = GetFontStyles()); }
}
public static ICollection<FontFamily> FontFamilies
{
get { return fontFamilies ?? (fontFamilies = GetFontFamilies()); }
}
}
xaml
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="fontFamiliesKey" ObjectType="{x:Type local:FontsWrapper}" MethodName="GetFontFamilies"/>
<ObjectDataProvider x:Key="fontWeightsKey" ObjectType="{x:Type local:FontsWrapper}" MethodName="GetFontStyles"/>
<ObjectDataProvider x:Key="fontStylesKey" ObjectType="{x:Type local:FontsWrapper}" MethodName="GetFontWeights"/>
</Window.Resources>
<StackPanel>
<ComboBox ItemsSource="{Binding Source={StaticResource fontFamiliesKey}}"></ComboBox>
<ComboBox ItemsSource="{Binding Source={StaticResource fontWeightsKey}}"></ComboBox>
<ComboBox ItemsSource="{Binding Source={StaticResource fontStylesKey}}"></ComboBox>
<!-- or Bind the Lists of wrapper class Directly -->
<ComboBox ItemsSource="{Binding Source={x:Static local:FontsWrapper.FontFamilies}}"></ComboBox>
<ComboBox ItemsSource="{Binding Source={x:Static local:FontsWrapper.FontStyles}}"></ComboBox>
<ComboBox ItemsSource="{Binding Source={x:Static local:FontsWrapper.FontWeights}}"></ComboBox>
</StackPanel>
I hope this will help.
In the end I just declared arrays as resources (with the options that I wanted to provide to the end user) in my XAML like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
xmlns:win="clr-namespace:System.Windows;assembly=PresentationCore"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ObjectDataProvider x:Key="FontFamilyOptions" ObjectType="{x:Type media:Fonts}" MethodName="get_SystemFontFamilies"/>
<x:Array x:Key="FontWeightOptions" Type="win:FontWeight">
<win:FontWeight>Normal</win:FontWeight>
<win:FontWeight>Bold</win:FontWeight>
<win:FontWeight>ExtraBold</win:FontWeight>
</x:Array>
<x:Array x:Key="FontStyleOptions" Type="win:FontStyle">
<win:FontStyle>Normal</win:FontStyle>
<win:FontStyle>Italic</win:FontStyle>
<win:FontStyle>Oblique</win:FontStyle>
</x:Array>
</ResourceDictionary>
And I used these as the item sources for my combo boxes like this:
<ComboBox ItemsSource="{Binding Source={StaticResource FontFamilyOptions}}" />
<ComboBox ItemsSource="{Binding Source={StaticResource FontWeightOptions}}" />
<ComboBox ItemsSource="{Binding Source={StaticResource FontStyleOptions}}" />

Closing-EventTrigger on a WPF Window - Problems with DataContext

I have a view, which initializes a viewmodel inside the windows resources. Further more I give my grid the DataContext.
My question is, how I can add a command to my windows closing event keeping mvvm in memory? I tried the version of this post:
Handling the window closing event with WPF / MVVM Light Toolkit
... but its not working using an event-trigger, because I can't access the viewmodel from outside my grid, so I can't access my command.
Any solution for my problem?
Greetings
Jannik
Edit: Here's my xaml:
<Window x:Class="WpfApplication1.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:WpfApplication1.ViewModels"
xmlns:converter="clr-namespace:WpfApplication1.Converter"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<viewModels:MainWindowViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource ViewModel}">...</Grid>
</Window>
You can reference to members of a static resource this way:
Command="{Binding Path=CloseCommand, Source={StaticResource ViewModel}}"
Here's the complete test project. I used a text box with a binding to ensure data is saved.
<Window x:Class="WpfApplication1.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:WpfApplication1.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<viewModels:MainWindowViewModel x:Key="ViewModel"/>
</Window.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<i:InvokeCommandAction Command="{Binding Path=CloseCommand, Source={StaticResource ViewModel}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid DataContext="{StaticResource ViewModel}">
<TextBox Text="{Binding Txt, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
In ViewModel code, I used a static reference to store data (LastInstance). you can replace it with your own method.
Also I used Command which is a custom implementation of ICommand. If you want I can add the complete implementation here.
public MainWindowViewModel()
{
//save or load here...
if (LastInstance != null) Txt = LastInstance.Txt;
CloseCommand = new Command(o => LastInstance = this);
//...
}
public static ViewModel LastInstance;
//Txt Dependency Property
public string Txt
{
get { return (string)GetValue(TxtProperty); }
set { SetValue(TxtProperty, value); }
}
public static readonly DependencyProperty TxtProperty =
DependencyProperty.Register("Txt", typeof(string), typeof(ViewModel), new UIPropertyMetadata(null));
//CloseCommand Dependency Property
public Command CloseCommand
{
get { return (Command)GetValue(CloseCommandProperty); }
set { SetValue(CloseCommandProperty, value); }
}
public static readonly DependencyProperty CloseCommandProperty =
DependencyProperty.Register("CloseCommand", typeof(Command), typeof(ViewModel), new UIPropertyMetadata(null));
The typical approach to this problem is to have a MainViewModel and set the DataContext of you Window to it. Then define other viewModels in the MainViewModel.
<Window>
<Grid DataContext="{Binding MyGridViewModel}">
</Grid>
<DockPanel DataContext="{Binding AnotherViewModel}">
</DockPanel>
</Window>
in MainWindow constructor:
this.DataContext = new MainViewModel();
in MainViewModel constructor:
this.MyGridViewModel = new OtherViewModel();
This way you have many options to find the desired object through viewModel references.

How to Expose a Control in XAML

I'm new in WPF and I'm creating a control. This control contains a DataGrid and some other WPF controls.
I created my Control as below:
<UserControl x:Class="MyControls.MyControl"
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="329" d:DesignWidth="535" >
<Grid>
<DataGrid Margin="6,25,6,35" Name="dataGrid" SelectionUnit="CellOrRowHeader" x:FieldModifier="public" HeadersVisibility="All"/>
<OtherControl HorizontalAlignment="Left" x:Name="otherControl" Height="34" VerticalAlignment="Bottom" Width="523" x:FieldModifier="private"/>
<Label Content="caption" Height="24" HorizontalAlignment="Left" Name="captionLabel" VerticalAlignment="Top" Foreground="#FF2626D1" x:FieldModifier="private"/>
</Grid>
</UserControl>
So, everything goes well so far, then I create a container UserControl which has in it my control created previously:
<UserControl x:Class="MyContainers.MyContainer"
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" xmlns:my="clr-namespace:MyControls">
<Grid>
<my:MyControl>
</my:MyControl>
</Grid>
What I can not do is the following:
<my:MyControl>
<my:MyControl.dataGrid>
</my:MyControl.dataGrid>
</my:MyControl>
I previously set the datagrid's property of FieldModifier as public in order to get access to it in another xaml, but it raises an error from visual studio.
I need to "expose" my dataGrid in order to be able to add columns and their styles.
I would like to be able to do something like this:
<my:MyControl.dataGrid.Columns >
<DataGridTextColumn />
<DataGridTextColumn />
...
<DataGridTextColumn />
</my:MyControl.dataGrid.Columns>
So, is not enough to set the datagrid's property of FieldModifier as public?
Do I need to do something else? How can I achieve this? Is this even possible?
I hope someone can help me. Thank you in advance.
You cannot access the child DataGrid as MyControl.dataGrid -- MyControl has no property named "dataGrid".
You might try adding a dependency property of type ObservableCollection<DataGridColumn> to MyControl, and modify the dataGrid columns whenever that collection changes.
EDIT:
Whipped together a quick example for you:
UserControl code:
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(ObservableCollection<DataGridColumn>), typeof(UserControl1));
public ObservableCollection<DataGridColumn> Columns
{
get { return (ObservableCollection<DataGridColumn>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
public UserControl1()
{
Columns = new ObservableCollection<DataGridColumn>();
Columns.CollectionChanged += (s, a) =>
{
dataGrid.Columns.Clear();
foreach (var column in this.Columns)
dataGrid.Columns.Add(column);
};
InitializeComponent();
}
}
UserControl xaml:
<UserControl x:Class="WpfApplication1.UserControl1"
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">
<Grid>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False"/>
</Grid>
so you can use it like:
<Grid>
<l:UserControl1>
<l:UserControl1.Columns>
<DataGridTextColumn Header="Col1"/>
<DataGridTextColumn Header="Col2"/>
</l:UserControl1.Columns>
</l:UserControl1>
</Grid>

Fail to do binding to image

i have simple class
public class A
{
public ImageSource imageSource
{
get;
set;
}
}
page class:
Public class page : Page
{
A a_class = new A();
}
And simple silverlight page that contain object type A.
In this page i have Image that i want to bind to imageSource of A.
So i wrote it and its not working.
<Image x:Name="Image_" Stretch="Fill"
Source="{Binding imageSource}" DataContext="{StaticResource a_class }"/>
How i need to write it so it will work fine ?
Thanks for any help.
The StaticResource markup extension doed not access fields or properties of the class that the Xaml is loaded into. Delete the line:-
A a_class = new A();
Instead instance A in a resource dictionary:-
<UserControl x:Class="YourApplication.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:local="clr-namespace:YourApplication"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:A x:Key="a_class" />
</UserControl>
<Grid x:Name="LayoutRoot">
<Image x:Name="Image_" Stretch="Fill"
Source="{Binding imageSource}" DataContext="{StaticResource a_class}"/>
</Grid>
</UserControl>
Note is you want the Image control to track changes made to the imageSource property you need A to implement INotifyPropertyChanged.

WPF -- Anyone know why I can't get this binding to reference?

<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3"
DataContext="{Binding Mode=OneWay, Source={StaticResource local:oPatients}}">
I'm getting StaticResource reference 'local:oPatients' was not found.
Here is the codebehind:
public partial class MainWindow : Window
{
ListBox _activeListBox;
clsPatients oPatients;
public MainWindow()
{
oPatients = new clsPatients(true);
...
To be able to address the object as a StaticResource, it needs to be in a resource dictionary. However, since you're creating the object in MainWindow's constructor, you can set the DataContext in the code-behind like so.
oPatients = new clsPatients(true);
stkWaitingPatients.DataContext = oPatients;
And then change the Binding to this:
{Binding Mode=OneWay}
This is an ok practice if you're not going to be changing the DataContext again, otherwise you'd want a more flexible solution.
Edit: You mentioned ObjectDataProvider in your comment. Here's how you'd do that. First, add an xmlns:sys to the Window for the System namespace (I'm assuming you already have one for xmlns:local):
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Then you can add an ObjectDataProvider to your resource dictionary like this:
<Window.Resources>
<ObjectDataProvider
x:Key="bindingPatients"
ObjectType="{x:Type local:clsPatients}">
<ObjectDataProvider.ConstructorParameters>
<sys:Boolean>True</sys:Boolean>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</Window.Resources>
And refer to it in a Binding with the StaticResource markup like this, using the same string we specified in the x:Key attached property we gave it in the dictionary:
{Binding Source={StaticResouce bindingPatients}, Mode=OneWay}
Edit 2: Ok, you posted more code in your answer, and now I know why it's throwing an exception during the constructor. You're attempting to do this...
lstWaitingPatients.DataContext = oPatients;
... but lstWaitingPatients doesn't actually exist until after this.InitializeComponent() finishes. InitializeComponent() loads the XAML and does a bunch of other things. Unless you really need to do something before all of that, put custom startup code after the call to InitalizeComponent() or in an event handler for Window's Loaded event.
The following sets the ItemsSource in Code Behind and correctly handles the DataBinding:
public partial class MainWindow : Window
{
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//assuming oPatients implements IEnumerable
this.lstWaitingPatients.ItemsSource = oPatients;
And the XAML:
<ListBox x:Name="lstWaitingPatients"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291"
ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
Now, I can't get this to work...I get a general Windows startup error.
Here is the codebehind with the Initializer and the class being instantiated:
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
lstWaitingPatients.DataContext = oPatients;
this.InitializeComponent();
Here's the top of my XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Orista_Charting"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
x:Class="Orista_Charting.MainWindow"
x:Name="windowMain"
Title="Orista Chart"
Width="1024" Height="768" Topmost="True" WindowStartupLocation="CenterScreen" Activated="MainWindow_Activated" >
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/ButtonStyles.xaml"/>
<ResourceDictionary Source="Resources/OtherResources.xaml"/>
<ResourceDictionary Source="Resources/TextBlockStyles.xaml"/>
<ResourceDictionary Source="Resources/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
Here's the pertinent XAML, as you see, I went ahead and moved the DataContext down to the ListBox from the StackPanel. This doesn't run, but it does render in Design View (however, with no data present in the ListBox):
<!-- Waiting Patients List -->
<Border BorderThickness="1,1,1,1" BorderBrush="#FF000000" Padding="10,10,10,10"
CornerRadius="10,10,10,10" Background="#FFFFFFFF" Margin="15.245,187.043,0,41.957" HorizontalAlignment="Left" >
<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Waiting Patients:" VerticalAlignment="Center" FontSize="21.333" Margin="0,0,0,20"/>
<TextBlock HorizontalAlignment="Right" Margin="0,0,38.245,0" Width="139" Height="16"
Text="Minutes Waiting" TextWrapping="Wrap" Foreground="#FF9C2525" FontWeight="Bold" VerticalAlignment="Bottom"
TextAlignment="Right"/>
<!-- Too be implemented, this is the wait animation -->
<!--<Image x:Name="PollGif" Visibility="{Binding Loading}"
HorizontalAlignment="Left" Margin="100,0,0,0" Width="42.5" Height="42.5"
Source="Images/loading-gif-animation.gif" Stretch="Fill"/>-->
</StackPanel>
<ListBox x:Name="lstWaitingPatients"
DataContext="{Binding Mode=OneWay}" ItemsSource="{Binding Mode=OneWay}"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291" ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
</StackPanel>
</Border>
Ok, but if I just take comment out the assigment line in the codebehind, it does run (albeit with no data in the listbox):
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//lstWaitingPatients.DataContext = oPatients;
THANKS!

Resources