Change WPF window Size programmatically in MVVM - c#

How do you change the size of your Window in WPF programmatically using an MVVM approach?
I am setting the window height to 400 from XAML and on click of a button on the form trying to increase the height to 500.
In my button's ICommand I am using:
Application.Current.MainWindow.Height = 500;
But it's not doing anything.

Try setting the 'Application.Current.MainWindow' property in the Loaded event in the MainWindow.xaml.cs file:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Application.Current.MainWindow = this;
}
UPDATE >>>
My friend, please remember, you said you wanted to use Application.Current.MainWindow... this is how you could use it. However, if you want to do this the MVVM way, then why don't you just bind the value to the Window.Width properties?
<Window Width="{Binding Width}" MinWidth="{Binding Width}" MaxWidth="{Binding Width}">
...
</Window>
Please note that binding to Window.Width is not enough for this to work.

I had a similar requirement to this, with the addition that I wanted to track some other window settings. So, I have this class:
public class WindowSettings
{
public int Width { get; set; }
public int Height { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
Then in my view model, I have:
public WindowSettings WindowSettings
{
get
{
return _windowSettings;
}
}
and in the xaml, this:
<Window ...
Height="{Binding WindowSettings.Height,Mode=TwoWay}"
Width="{Binding WindowSettings.Width, Mode=TwoWay}"
Left="{Binding WindowSettings.Left,Mode=TwoWay}"
Top="{Binding WindowSettings.Top,Mode=TwoWay}">
If I wanted to programmatically update the width, for example, I do this:
WindowSettings.Width = 456;
RaisePropertyChangedEvent("WindowSettings.Width");
(Naturally, my view model inherits from a base class which implements INotifyPropertyChanged.)
I did toy with the idea of providing an event on the WindowSettings class so that an update to a property could automatically cause the property change notification, but my requirement is to track the window size and set it on startup, so I didn't bother. Hope that helps.

Using VS 2017 and Caliburn Micro I have no issue with just setting the width. The way you are doing it isn't MVVM your VM doesn't need to know about the V or what's happening when the int Width changes.
public class ShellViewModel : Caliburn.Micro.PropertyChangedBase, IShell
{
public ShellViewModel()
{
Width = 500;
}
private int _width;
public int Width
{
get => _width;
set
{
_width = value;
NotifyOfPropertyChange(() => Width);
}
}
public void MyButton()
{
Width = 800;
}
}
In the Window XAML
Width="{Binding Width, Mode=TwoWay}"
In the Grid
<Button x:Name="MyButton" Content="Make 800 wide" />
it opens at 500 and when I click the button it stretches to 800.

I'm adding this answer because the most voted answer actually isn't correct. Binding to the MinHeight & MaxHeight, will result in a window that can't be resized using the grips.
<Window Width="{Binding Width}" MinWidth="{Binding Width}" MaxWidth="{Binding Width}">
...
</Window>
Instead add Mode=TwoWay to your binding.
<Window Width="{Binging Width, Mode=TwoWay}">
...
</Window>

Are you sure the window you want to resize is MainWindow? Or are you sure your command is successfully binding on your button? You can try the below code:
View:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:WpfApplication1.ViewModel"
Title="Wpf Application" Height="300" Width="400"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<viewModel:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Button Content="Resize" Command="{Binding ResizeCommand}" IsEnabled="{Binding ResizeCommand.CanExecute}" />
</Grid>
</Window>
ViewModel:
public class MainWindowViewModel
{
private RelayCommand _resizeCommand;
public RelayCommand ResizeCommand { get { return _resizeCommand; } }
public MainWindowViewModel()
{
this._resizeCommand = new RelayCommand(this.Resize);
}
private void Resize()
{
Application.Current.MainWindow.Height = 500;
}
}

This Works
Application.Current.MainWindow = this;
Application.Current.MainWindow.WindowState = WindowState.Normal;
Application.Current.MainWindow.Width = 800;
Application.Current.MainWindow.Height = 450;

Related

Wrong startup position of window, when SizeToContent is Manual, and Window.Top and Window.Left are databound

I've ran into a rather strange issue. It seems that as long as a window's SizeToContent is set to Manual and the dimensions and top/left properties are databound, the Presentation Framework won't update the location of the window when it is shown.
I have a Dialog Window-control which I use to display other UserControls within, and the behavior should be controlled by a ViewModel. Some dialogs I wish to display with SizeToContent.WidthAndHeight and ResizeMode.NoResize, and center them on the Owner-window. Other controls I wish to show in a dialog that can be resized, but opened with a specific size.
I made a test-project just to isolate the issue. Here is my Dialog:
<Window x:Class="DialogTests.Dialog"
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:local="clr-namespace:DialogTests"
mc:Ignorable="d"
ResizeMode="CanResize"
SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterOwner"
Width="{Binding Path=FormWidth, Mode=TwoWay}"
Height="{Binding Path=FormHeight, Mode=TwoWay}"
Top="{Binding Path=FormTop, Mode=TwoWay}"
Left="{Binding Path=FormLeft, Mode=TwoWay}"
Title="Dialog">
<StackPanel>
<Button Height="100">Button 1</Button>
<Button Width="100">Button 2</Button>
</StackPanel>
</Window>
public partial class Dialog : Window
{
public Dialog()
{
InitializeComponent();
this.DataContext = new DialogVm();
}
}
public class DialogVm : INotifyPropertyChanged
{
private double _formWidth;
private double _formHeight;
private double _formTop;
private double _formLeft;
public DialogVm()
{
FormWidth = 400;
FormHeight = 400;
}
public double FormWidth
{
get { return _formWidth; }
set
{
if (value.Equals(_formWidth)) return;
_formWidth = value;
OnPropertyChanged();
}
}
public double FormHeight
{
get { return _formHeight; }
set
{
if (value.Equals(_formHeight)) return;
_formHeight = value;
OnPropertyChanged();
}
}
public double FormTop
{
get { return _formTop; }
set
{
if (value.Equals(_formTop)) return;
_formTop = value;
OnPropertyChanged();
}
}
public double FormLeft
{
get { return _formLeft; }
set
{
if (value.Equals(_formLeft)) return;
_formLeft = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The reason for having the Dialog databind to these properties is to save the state (dimensions and position), so that it can be used in cases where certain dialogs should be opened using these previous values.
In my main application I just open the dialog by pressing a button:
private void Button_Click(object sender, RoutedEventArgs e)
{
var dialog = new Dialog();
dialog.Owner = this;
dialog.ShowDialog();
}
If you run this, then everything works as expected. The dialog gets sized to fit the contents, and is placed in the center relative to the main application. The setters of FormWidth, FormHeight, FormTop and FormLeft get called. For the dimensions, it's first called from the constructor, then by the PresentationFramework.
However, if you go back to the Dialog xaml, and change SizeToContent="WidthAndHeight" to SizeToContent="Manual", the Dialog will be shown at the top left (0,0) of the screen. The dialog's dimensions are set in the view model contructor (400 x 400) and doesn't get called again (as expected). The issue is, the Setter methods of FormTop and FormLeft are never called. The expected behaviour is that the 400x400 window should be shown in the center of the Owner window.
If I change the Dialog.xaml to remove the databindings, and explicitly set the dimensions, it works as it should though:
<Window x:Class="DialogTests.Dialog"
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:local="clr-namespace:DialogTests"
mc:Ignorable="d"
ResizeMode="CanResize"
SizeToContent="Manual"
WindowStartupLocation="CenterOwner"
Width="400"
Height="400"
Title="Dialog">
...
</Dialog>
Is this behavior a bug? Or is there something I am missing? Seems so strange that it works fine as long as SizeToContent is set to WidthAndHeight, or if I remove the bindings entirely and set the attributes in the xaml.
It depends on when the bindings are resolved and when the position of the window is set.
You could set the DataContext of the Dialog window before you call the InitializeComponent() method:
public Dialog()
{
this.DataContext = new DialogVm();
InitializeComponent();
}
Or set the WindowStartupLocation to Manual and calculate the values of your Left and Top source properties:
How do you center your main window in WPF?

Window height and width binding: Only one is fetched from DataContext

I've been googling this without getting the issue of that:
Simple xaml:
<Window x:Class="WpfHeightBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wpfHeightBinding="clr-namespace:WpfHeightBinding"
Title="MainWindow"
Width="{Binding Width}"
Height="{Binding Height}"
SizeToContent="Manual"
>
<Window.DataContext>
<wpfHeightBinding:TheDataContext />
</Window.DataContext>
<Grid>
</Grid>
</Window>
I bind both Width and Height to the data context. Here's the cs code
using System.Windows;
namespace WpfHeightBinding
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class TheDataContext
{
public int Width
{
get { return 200; }
}
public int Height
{
get { return 400; }
}
}
}
I set breakpoints to both getters. Only the first one is fetched, Height is ignored. If I switch Height and Width in xaml, then only the Height is fetched and Width is ignored.
I really cannot explain this behaviour. The window's height seems to be arbitrary. I have no MinHeight, nothing else influencing it, what happens to the binding? There is no error. SizeToContent has no effect.
I would understand if none of them are used as they might have been fetched before DataContext has been initialized but the DataContext is queried.
You need to give your 'Width' and 'Height' properties setters in code-behind - and then make your bindings 'TwoWay':
XAML
<Window x:Class="WpfHeightBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wpfHeightBinding="clr-namespace:WpfHeightBinding"
Title="MainWindow"
Width="{Binding Width, Mode=TwoWay}"
Height="{Binding Height, Mode=TwoWay}"
SizeToContent="Manual"
>
<Window.DataContext>
<wpfHeightBinding:TheDataContext />
</Window.DataContext>
Code-behind:
public class TheDataContext
{
public int Width { get { return 800; } set { } }
public int Height { get { return 200; } set { } }
}

WPF Button Command binding

I have a MainWindow:Window class which holds all the data in my program. The MainWindow's .xaml contains only an empty TabControl which is dynamically filled (in the code-behind).
One of the tabs (OptionsTab) has its .DataContext defined as the MainWindow, granting it access to all of the data. The OptionsTab has a DataGrid which has a column populated with Buttons, as shown below:
The DataGrid is populated with DataGridTemplateColumns, where the DataTemplate is defined in the main <Grid.Resources>. I would like to bind this button to a function in the MainWindow (not the OptionsTab in which it resides).
When the OptionsTab is created, it's .DataContext is set as the MainWindow, so I would have expected that defining the DataTemplate as below would have done it.
<DataTemplate x:Key="DeadLoadIDColumn">
<Button Content="{Binding Phases, Path=DeadLoadID}" Click="{Binding OpenDeadLoadSelector}"/>
</DataTemplate>
I thought this would mean the Click event would be bound to the desired OptionsTab.DataContext = MainWindow's function.This, however, didn't work (the Content did, however). So then I started looking things up and saw this answer to another SO question (by Rachel, who's blog has been of great help for me), from which I understood that you can't {bind} the click event to a method, but must instead bind the Command property to an ICommand property (using the helper RelayCommand class) which throws you into the desired method. So I implemented that, but it didn't work. If I place a debug breakpoint at the DeadClick getter or on OpenDeadLoadSelector() and run the program, clicking on the button doesn't trigger anything, meaning the {Binding} didn't work.
I would like to know if this was a misunderstanding on my part or if I simply did something wrong in my implementation of the code, which follows (unrelated code removed):
MainWindow.xaml
<Window x:Class="WPF.MainWindow"
x:Name="Main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPF" SizeToContent="WidthAndHeight">
<TabControl Name="tabControl"
SelectedIndex="1"
ItemsSource="{Binding Tabs, ElementName=Main}">
</TabControl>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
ICommand deadClick;
public ICommand DeadClick
{
get
{
if (null == deadClick)
deadClick = new RelayCommand(p => OpenDeadLoadSelector());
return deadClick;
}
}
public ObservableCollection<TabItem> Tabs = new ObservableCollection<TabItem>();
public static DependencyProperty TabsProperty = DependencyProperty.Register("Tabs", typeof(ICollectionView), typeof(MainWindow));
public ICollectionView ITabsCollection
{
get { return (ICollectionView)GetValue(TabsProperty); }
set { SetValue(TabsProperty, value); }
}
public ObservableCollection<NPhase> Phases = new ObservableCollection<NPhase>();
public static DependencyProperty PhasesProperty = DependencyProperty.Register("Phases", typeof(ICollectionView), typeof(MainWindow));
public ICollectionView IPhasesCollection
{
get { return (ICollectionView)GetValue(PhasesProperty); }
set { SetValue(PhasesProperty, value); }
}
public ObservableCollection<string> Loads = new ObservableCollection<string>();
public static DependencyProperty LoadsProperty = DependencyProperty.Register("Loads", typeof(ICollectionView), typeof(MainWindow));
public ICollectionView ILoadsCollection
{
get { return (ICollectionView)GetValue(LoadsProperty); }
set { SetValue(LoadsProperty, value); }
}
void OpenDeadLoadSelector()
{
int a = 1;
}
public MainWindow()
{
var optionsTab = new TabItem();
optionsTab.Content = new NOptionsTab(this);
optionsTab.Header = (new TextBlock().Text = "Options");
Tabs.Add(optionsTab);
ITabsCollection = CollectionViewSource.GetDefaultView(Tabs);
Loads.Add("AS");
Loads.Add("2");
InitializeComponent();
}
}
OptionsTab.xaml
<UserControl x:Class="WPF.NOptionsTab"
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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
xmlns:l="clr-namespace:WPF">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="DeadLoadIDColumn">
<Button Content="{Binding Phases, Path=DeadLoadID}" Command="{Binding Path=DeadClick}"/>
</DataTemplate>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<!-- ... -->
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<!-- ... -->
</Grid>
<Grid Grid.Row="1">
<!-- ... -->
</Grid>
<l:NDataGrid Grid.Row="2"
x:Name="PhaseGrid"
AutoGenerateColumns="False">
<l:NDataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}"/>
<DataGridTextColumn Header="Date (days)" Binding="{Binding Path=Date}"/>
<DataGridTemplateColumn Header="Deadload" CellTemplate="{StaticResource DeadLoadIDColumn}"/>
</l:NDataGrid.Columns>
</l:NDataGrid>
</Grid>
</UserControl>
OptionsTab.xaml.cs
public NOptionsTab(MainWindow w)
{
DataContext = w;
InitializeComponent();
PhaseGrid.ItemsSource = w.Phases;
}
While we're at it (and this might be a related question), why does {Binding Phases, Path=DeadLoadID} work on the DataTemplate (which is why the buttons appear with "Select"), but if I do {Binding Phases, Path=Name} in the PhaseGrid and remove the .ItemsSource code from the constructor, nothing happens? Shouldn't the PhaseGrid inherit its parent's (NOptionsTab / Grid) DataContext? Hell, even setting PhaseGrid.DataContext = w; doesn't do anything without the .ItemsSource code.
EDIT (27/04/14):
I think that knowing the contents of the NPhase class itself will be of use, so here it is:
public class NPhase : INotifyPropertyChanged
{
string name;
double date;
string deadLoadID = "Select";
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get { return name; }
set
{
name = value;
EmitPropertyChanged("Name");
}
}
public double Date
{
get { return date; }
set
{
date = value;
EmitPropertyChanged("Date");
}
}
public string DeadLoadID
{
get { return deadLoadID; }
set
{
deadLoadID = value;
EmitPropertyChanged("DeadLoadID");
}
}
void EmitPropertyChanged(string property)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public NPhase(double _date, string _name)
{
date = _date;
name = _name;
}
}
EDIT (29/04/14):
A simplified project (getting rid of everything that wasn't necessary) can be downloaded from here (https://dl.dropboxusercontent.com/u/3087637/WPF.zip)
I think that there is the problem that you do not specify data source properly for the data item inside your grid.
I think that the data source for your button column is NPhase instance. So it has no DeadClick property. So, you can check it using Output window in Visual Studio.
I suggest that you can do something like that:
<DataTemplate x:Key="DeadLoadIDColumn">
<Button Content="{Binding Phases, Path=DeadLoadID}"
Command="{Binding Path=DataContext.DeadClick, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type l:NDataGrid}}}"/>
</DataTemplate>
I currently do not understand how you can compile Content="{Binding Phases, Path=DeadLoadID}", because as I thought the default value for Binding clause is the Path property, and you have specified it twice.
EDIT
After I got the small solution all becomes clear. Here is the modified solution. All what I changed in it - I have added RelativeSource to the command binding as I described above, and I added MainWindow as DataContext for your OptionsTab (you have specified it in the question, but not in the project). That's it - all works fine - the command getter is called, and the command is executed when you click the button.

Creating a custom image class in WPF following MVVM pattern

Im just starting out with MVVM and at the moment still find alot of things confusing.
So I am trying to keep things as simple as I can at the moment.
I am trying to write code for a custom image which later will be able to be placed on a canvas control by a user at runtime. I'm trying to use MVVM so that I will be able to save and reload the content on a canvas.
I have created a model class called CustomImage with the following code:
namespace StoryboardToolMvvm
{
public class CustomImage
{
public Uri imageLocation { get; set; }
public BitmapImage bitmapImage { get; set; }
}
}
I have a modelview class as follows:
namespace StoryboardToolMvvm
{
class CustomImageViewModel : ViewModelBase
{
private CustomImage _customImage;
private ObservableCollection<CustomImage> _customImages;
private ICommand _SubmitCommand;
public CustomImage CustomImage
{
get { return _customImage; }
set
{
_customImage = value;
NotifyPropertyChanged("CustomImage");
}
}
public ObservableCollection<CustomImage> CustomImages
{
get { return _customImages; }
set
{
_customImages = value;
NotifyPropertyChanged("CustomImages");
}
}
public ICommand SubmitCommand
{
get
{
if (_SubmitCommand == null)
{
_SubmitCommand = new RelayCommand(param => this.Submit(), null);
}
return _SubmitCommand;
}
}
public CustomImageViewModel()
{
CustomImage = new CustomImage();
CustomImages = new ObservableCollection<CustomImage>();
CustomImages.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(CustomImages_CollectionChanged);
}
private void CustomImages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("CustomImages");
}
private void Submit()
{
CustomImage.imageLocation = new Uri(#"H:\My Pictures\whale.png");
CustomImage.bitmapImage = new BitmapImage(CustomImage.imageLocation);
CustomImages.Add(CustomImage);
CustomImage = new CustomImage();
}
}
}
And a view class:
<UserControl x:Class="StoryboardToolMvvm.CustomImageView"
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:viewmodel="clr-namespace:StoryboardToolMvvm"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<viewmodel:CustomImageViewModel x:Key="CustomImageViewModel"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource CustomImageViewModel}}">
<Image Source="{Binding CustomImage.bitmapImage, Mode=TwoWay}" Width="150" Height="150" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="75,50,0,0" />
<Button Content="Submit" Command="{Binding SubmitCommand}" Width="100" Height="50" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,20" />
</Grid>
</UserControl>
I add this view to my MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StoryboardToolMvvm" x:Class="StoryboardToolMvvm.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CustomImageView HorizontalAlignment="Left" Height="100" Margin="181,110,0,0" VerticalAlignment="Top" Width="100"/>
</Grid>
</Window>
I am very unsure as to whether I am on the right lines here with a MVVM pattern so any comments would be much appreciated. Also when Submit is pressed I would have expected my image to load but this does not happen can anyone advise as to why?
Many Thanks in advance..
As far as my understanding of MVVM and your question goes, I have one main comment about your code.
I think your CustomImage is actually both Model and ViewModel layer, and you should split it in two :
the Model, which would contain the path itself ;
the ViewModel, which contain the BitmapImage and initialize it from the Model and constructing time.
The path is the mere data used for saving, and it fits the Model, whereas the BitmapImage is how the data is shown and should be constructed in the ViewModel.
One advantage is that now, your BitmapImage gets its own NotifyPropertyChanged call at setting time, and you won't have anymore problem or a View part directly bound to the Model.
As for your CustomImageViewModel, this looks like more of a MainViewModel-ish thing. You can still use this to store the ViewModels.

WPF List of Groups of Controls Databound to List of Custom Class?

I'm still somewhat new to WPF (only done a few small projects with it). I'm trying to make a repeating group of controls (user can add/remove these groups), databound to a custom class. Example UI:
([UserButton1] [UserButton2]) <--each of these () is a separate group of buttons
([Cheese] [Wine] )
([Wallace] [Gromit] )
[Add] <--this button can add more groups
databound to a list of a class like this (pseudocode):
class UserButtons {
string UserButton1 = "UserButton1"
string UserButton2 = "UserButton2"
}
such as
List<UserButtons> = {
[0]: UserButton1, UserButton2
[1]: Cheese, Wine
[2]: Wallace, Gromit
}
I know this is the sort of thing WPF was created to do, but I can't quite figure out exactly how to go about it.
Should I use some sort of ListView? Would a DataTemplate help? A StackPanel sounds OK, but it doesn't have databinding for a list...or does it? And I'm not even sure how to make databinding work for the groups of buttons like indicated above (if that even made sense to you...sorry for the bad example). Does anyone have any insight on this problem?
I searched to try to find a question pertaining to this and didn't see one, perhaps because I wasn't sure what to search for. So, sorry if it's an unintended dupe.
I'm not entirely sure what you're looking for but I hope the example below helps. I used an ItemsControl whose ItemsSource is set to the collection of UserButtons. Its ItemTemplate property is set to a StackPanel that shows two buttons, the Content property of each is bound to properties in UserButtons.
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>
</Window.Resources>
<StackPanel Orientation="Vertical">
<ItemsControl x:Name="itemsControl" Background="LightBlue">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="{Binding Button1}" Width="100"/>
<Button Content="{Binding Button2}" Width="100"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Width="50" Click="Button_Click">Add</Button>
</StackPanel>
</Window>
Code-Behind:
public partial class MainWindow : Window
{
ObservableCollection<UserButtons> oc;
public MainWindow()
{
InitializeComponent();
oc = new ObservableCollection<UserButtons>()
{
new UserButtons() { Button1="UserButton1", Button2 = "UserButton2"},
new UserButtons() { Button1="Cheese", Button2 = "Wine"},
new UserButtons() { Button1="Wallace", Button2 = "Gromit"},
};
this.itemsControl.ItemsSource = oc;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
oc.Add(new UserButtons() { Button1 = "NewButton1", Button2 = "NewButton2" });
}
}
public class UserButtons : INotifyPropertyChanged
{
private string button1;
public string Button1
{
get { return this.button1; }
set
{
this.button1 = value;
this.OnPropertyChanged("Button1");
}
}
private string button2;
public string Button2
{
get { return this.button2; }
set
{
this.button2 = value;
this.OnPropertyChanged("Button2");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}

Categories

Resources