I am new to WPF, just got thrown on a project. There is already an existing LoginWindow, and currently there is a forgot password button which opens a new dialog window. The manager wants that changed to use the same window, instead of opening a new one on top of it. I haven't seen a really solid example of how to accomplish this, as there seems to be a lot of different ways to do things in WPF. My initial though was maybe to use a Panel like a div and show and hide them in the same window like I DIV in HTML but not sure.
The current login someone already built looks like this:
<Controls:MetroWindow x:Class="Omega.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:Omega.Properties"
xmlns:tools="clr-namespace:Omega.modules.Features"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="USER LOGIN" Icon="resources/icons/OmegaApp.ico"
TextOptions.TextFormattingMode="Display"
ShowInTaskbar="True" Topmost="False"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
GlowBrush="{DynamicResource AccentColorBrush}" BorderThickness="1" EnableDWMDropShadow="True" WindowTransitionsEnabled="False" Closed="LoginWindow_Close" Height="446" Width="808" Visibility="Visible">
<Controls:MetroWindow.Background>
<ImageBrush ImageSource="resources\images\Omega-BGScreen.jpg"/>
</Controls:MetroWindow.Background>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/resources/Resource_Dictionary.xaml"/>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatButton.xaml" />
<ResourceDictionary Source="/resources/Icons.xaml" />
</ResourceDictionary.MergedDictionaries>
<x:Array x:Key="WindowCommandsOverlayBehaviorArray"
Type="Controls:WindowCommandsOverlayBehavior">
<Controls:WindowCommandsOverlayBehavior>Always</Controls:WindowCommandsOverlayBehavior>
<Controls:WindowCommandsOverlayBehavior>Flyouts</Controls:WindowCommandsOverlayBehavior>
<Controls:WindowCommandsOverlayBehavior>HiddenTitleBar</Controls:WindowCommandsOverlayBehavior>
<Controls:WindowCommandsOverlayBehavior>Never</Controls:WindowCommandsOverlayBehavior>
</x:Array>
</ResourceDictionary>
</Window.Resources>
<Grid >
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Grid.ColumnSpan="3" Source="resources/images/Omega_Logo.png" HorizontalAlignment="Left" Height="100" Margin="58,48,0,0" VerticalAlignment="Top" Width="186" Grid.RowSpan="2"/>
<TextBox Name="UserID_Text" Grid.ColumnSpan="2" Grid.Column="2" HorizontalAlignment="Left" Height="30" Grid.Row="2" TextWrapping="Wrap" Text="Omega" VerticalAlignment="Top" Width="190" VerticalContentAlignment="Center" Margin="0,30,0,0" TabIndex="0" IsTabStop="True"/>
<Button Style="{StaticResource StandardButton}" Content="{x:Static properties:Resources.Login_Label}" IsDefault="True" HorizontalAlignment="Left" Grid.Row="3" VerticalAlignment="Top" Width="400" Grid.ColumnSpan="4" Height="30" Grid.Column="2" Click="Login_Button_Click" TabIndex="2" />
<TextBox HorizontalAlignment="Left" Height="23" Margin="0,33,0,0" Grid.Row="5" TextWrapping="Wrap" Text="v 2.0" VerticalAlignment="Top" Width="52" RenderTransformOrigin="0.392,0.346" BorderThickness="0" Foreground="LightGray" Focusable="False"/>
<PasswordBox Name="Pass_Text" Password="nopassword" Grid.Column="4" HorizontalAlignment="Left" Margin="10,30,0,0" Grid.Row="2" VerticalAlignment="Top" Height="30" Grid.ColumnSpan="2" Width="190" VerticalContentAlignment="Center" TabIndex="1"/>
<Label Content="User Login" Grid.Column="2" HorizontalAlignment="Left" Grid.Row="2" VerticalAlignment="Top" Width="100"/>
<Button Style="{StaticResource LinkButton}" Content="{x:Static properties:Resources.ForgotPass_label}" Grid.Column="2" HorizontalAlignment="Left" Grid.Row="3" VerticalAlignment="Top" Click="ForgotPassword_Click" Width="Auto" Grid.ColumnSpan="2" Margin="0,35,0,0" Focusable="False"/>
<TextBlock Grid.Column="3" HorizontalAlignment="Left" Margin="18,36,0,0" Grid.Row="3" TextWrapping="Wrap" Text=" | " VerticalAlignment="Top" Width="Auto" Height="17" RenderTransformOrigin="2,0.529"/>
<Button Style="{StaticResource LinkButton}" Content="{x:Static properties:Resources.Help_label}" Grid.Column="3" HorizontalAlignment="Left" Margin="36,36,0,0" Grid.Row="3" VerticalAlignment="Top" Width="34" Height="17" Click="Help_Click" RenderTransformOrigin="0.559,1.824" Focusable="False"/>
</Grid>
</Controls:MetroWindow>
I'm sorry this is such a crude example but I hope it shows you how you could solve your problem. Below is a simple example on how to swap two user controls using a button...
You have a shell view:
<Window x:Class="LogonApplication.MainWindow"
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:LogonApplication"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ChangePasswordViewModel}">
<local:ChangePasswordView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:EnterPasswordViewModel}">
<local:EnterPasswordView />
</DataTemplate>
</Window.Resources>
<DockPanel LastChildFill="False">
<ContentPresenter Content="{Binding ActiveView}"
DockPanel.Dock="Top"/>
<StackPanel DockPanel.Dock="Bottom">
<Button Content="Change View" Command="{Binding ChangeViewCommand}"/>
</StackPanel>
</DockPanel>
</Window>
Bound to a view model like this:
public class MainWindowViewModel : ViewModelBase
{
ViewModelBase _activeView;
public MainWindowViewModel()
{
_activeView = new EnterPasswordViewModel();
ChangeViewCommand = new ChangeViewCommand(this);
}
public ViewModelBase ActiveView
{
get { return _activeView; }
set
{
_activeView = value;
OnPropertyChanged("ActiveView");
}
}
public ICommand ChangeViewCommand { get; set; }
}
Then all you need to do is change the viewmodel being displayed by your content presenter. This could be achieved by wiring up a command to toggle your user controls/view models.
class ChangeViewCommand : ICommand
{
private MainWindowViewModel _mainWindowViewModel;
public event EventHandler CanExecuteChanged;
public ChangeViewCommand(MainWindowViewModel mainWindowViewModel)
{
_mainWindowViewModel = mainWindowViewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (_mainWindowViewModel.ActiveView.GetType() == typeof(EnterPasswordViewModel))
{
_mainWindowViewModel.ActiveView = new ChangePasswordViewModel();
}
else
{
_mainWindowViewModel.ActiveView = new EnterPasswordViewModel();
}
}
}
Heres the base class for completeness:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I havent shown the other two views and view models but the views are just WPF User controls and the view models are classes that implement viewmodelbase. Let me know if you need more information.
Related
I need to change the color of a rectangle in a GridView when the item is selected.
Unselected item
Selected item
My Main Page in XAML.
<Page
x:Class="GridViewWithSelectedItem.Views.MainPage"
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:views="using:GridViewWithSelectedItem.Views"
Style="{StaticResource PageStyle}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="TileTemplate" x:DataType="views:Article">
<Border BorderThickness="2,2,2,2" BorderBrush="#FF868484" Margin="3,3,3,3" HorizontalAlignment="Stretch" MaxWidth="600" MinWidth="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="60"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Fill="Black" Grid.RowSpan="3"/>
<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="1">
<TextBlock FontSize="20" Margin="0,0,5,0" TextWrapping="Wrap" Foreground="DarkBlue" FontWeight="Bold" Text="{x:Bind Number}"/>
<TextBlock FontSize="20" TextWrapping="Wrap" Foreground="DarkBlue" Text="{x:Bind Title}" FontWeight="Bold"/>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="1" FontSize="20" Text="{x:Bind Description}" TextWrapping="WrapWholeWords"/>
<StackPanel Background="LightBlue" Padding="5,0,0,0" Grid.Row="2" Grid.Column="1">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="20" Margin="0,0,5,0" TextWrapping="Wrap">Date :</TextBlock>
<TextBlock Foreground="Red" FontSize="20" TextWrapping="Wrap" Text="{x:Bind Date}" FontWeight="Bold"/>
</StackPanel>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</Page.Resources>
<Grid x:Name="ContentArea" Margin="{StaticResource MediumLeftRightMargin}">
<Grid Background="White" VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="0,25,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="600"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="800"/>
</Grid.RowDefinitions>
<TextBlock Text="The Guardian" FontSize="35" FontWeight="Bold" Grid.ColumnSpan="2" HorizontalAlignment="Center"/>
<GridView
BorderThickness="2,2,0,2" BorderBrush="#FF868484"
MinWidth="600"
Grid.Column="0"
Grid.Row="1"
Padding="5,5,5,5"
HorizontalAlignment="Center"
CanDragItems="False"
IsItemClickEnabled="true"
IsTapEnabled="False"
IsSwipeEnabled="False"
ItemsSource="{x:Bind Articles}"
ItemTemplate="{StaticResource TileTemplate}"
SelectedItem="{x:Bind Mode=TwoWay, Path=SelectedArticle}"
/>
<RelativePanel Grid.Row="1" Grid.Column="1" Background="WhiteSmoke" BorderThickness="2" BorderBrush="#FF868484" Padding="10">
<Grid RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<TextBlock FontSize="20" Margin="0,0,5,0" TextWrapping="Wrap" Foreground="DarkBlue" FontWeight="Bold" Text="{x:Bind Path=SelectedArticle.Number, Mode=OneWay}"/>
<TextBlock FontSize="20" TextWrapping="Wrap" Foreground="DarkBlue" Text="{x:Bind Path=SelectedArticle.Title, Mode=OneWay}" FontWeight="Bold"/>
</StackPanel>
<TextBlock Grid.Row="1" FontSize="20" Text="{x:Bind Path=SelectedArticle.Description, Mode=OneWay}" TextWrapping="WrapWholeWords"/>
</Grid>
</Grid>
<RelativePanel Background="LightBlue" Padding="5,0,0,0" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="20" Margin="0,0,5,0" TextWrapping="Wrap">Date :</TextBlock>
<TextBlock Foreground="Red" FontSize="20" TextWrapping="Wrap" Text="{x:Bind Path=SelectedArticle.Date, Mode=OneWay}" FontWeight="Bold"/>
</StackPanel>
</RelativePanel>
</RelativePanel>
</Grid>
</Grid>
</Page>
And the class of my XAML Page.
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml.Controls;
namespace GridViewWithSelectedItem.Views
{
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public ObservableCollection<Article> Articles;
private Article _selectedArticle;
public Article SelectedArticle
{
get { return _selectedArticle; }
set { Set(ref _selectedArticle, value); }
}
public MainPage()
{
InitializeComponent();
Articles = new ObservableCollection<Article>();
Articles.Add(new Article(0, "Uighurs", "Being young' leads to detention in China's Xinjiang region", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(1, "Brexit", "Chances of Brexit deal hang on Boris Johnson and Ursula von der Leyen dinner", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(2, "Environment", "Secretive ‘gold rush’ for deep-sea mining dominated by handful of firms", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(3, "Juukan Gerge induiry", "Juukan Gorge inquiry: Rio Tinto's decision to blow up Indigenous rock shelters 'inexcusable'", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(4, "Australia", "British journalist uncovered Australian woman's alleged plan to kill parents on dark web, police say", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(5, "Coronavirus", "Nine out of 10 in poor nations to miss out on inoculation as west buys up Covid vaccines", DateTime.Parse("09/12/2020")));
}
public event PropertyChangedEventHandler PropertyChanged;
private void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class Article
{
public int Number { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
public Article(int number, string title, string description, DateTime date)
{
Number = number;
Title = title;
Description = description;
Date = Date;
}
}
}
I see AutomationProperty.name can help me but i don't understand how to use it.
I found a way to change the color of a selected item in my class but i need to recreate the collection and i cost lot of resources. I think its possible to make it in XAML code.
Edit: I made a simple exemple of my code.
OneDrive link
You could add a Brush property into the Article class, and bind the Brush property to Rectangle.Fill property of your DataTemplate to change the color when an item of GridView control is selected.
Please check the following code:
In DataTemplate of your xaml file:
<Rectangle x:Name="rectangle" Fill="{x:Bind Brush}" Grid.RowSpan="3"/>
……
<GridView …… SelectionChanged="GridView_SelectionChanged" />
In code-behind:
public class Article
{
……
public SolidColorBrush Brush { get; set; }
public Article(int number, string title, string description, DateTime date)
{
……
Brush = new SolidColorBrush(Colors.Black);
}
}
Add a _preSelectedArticle property to save the previous item in MainPage class:
private Article _preSelectedArticle;
Change the value of Brush property of selected item and previous selected item:
private void GridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(_preSelectedArticle==null)
{
SelectedArticle.Brush.Color = Colors.Green;
_preSelectedArticle = SelectedArticle;
}
if (_preSelectedArticle!=null&&_preSelectedArticle!= SelectedArticle)
{
_preSelectedArticle.Brush.Color = Colors.Black;
SelectedArticle.Brush.Color = Colors.Green;
_preSelectedArticle = SelectedArticle;
}
}
What I want to do is to be able to access an object's properties through a bound textblock that is found in a viewModel class. So say I want to access the OrderID of the Orders class in a bound textblock that is stored in a viewModel class.
What I have for that is:
<textblock text="{Binding Path=Order.OrderID}"/>
This is connected to the order's orderID as when I change the OrderID in the ViewModel class the change is reflected in the textblock. The problem occurs when I try to load the OrderID from another class.
The Other class:
public class ModifyOrder
{
private ViewModel boundData;
public ModifyOrder()
{
boundData = new ViewModel();
}
public void ChangeOrderID()
{
boundData.Order.OrderID = 10;
}
}
The changes here don't get transfered to the static _Order in the ViewModel class.
This is the viewModel class:
public class ViewModel : INotifyPropertyChanged
{
private Orders _Order;
public Orders Order
{
get { return _Order; }
set
{
if (_Order != value)
{
_Order = value;
}
}
}
public ViewModel()
{
Order = new Orders();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have the ViewModel class loaded into the UI's DataContext and my other bound variables in the ViewModel class work fine but for some reason either the bound textblock isn't accessing the Order.OrderID (Which I don't think is the problem since I can modify the Order.OrderID in the ViewModel class and the changes are reflected) OR the class that's modifying my Order isn't able to modify the OrderID.
I've already tried to load a new Order class with the new OrderID and then try to load the ViewModel's _Order with the ModifyOrder's new Order but that hasn't worked out either.
This is the Orders class:
public class Orders : INotifyPropertyChanged
{
private int _OrderID;
public int OrderID
{
get { return _OrderID; }
set
{
if(_OrderID != value)
{
_OrderID = Value
OnPropertyChanged(nameof(OrderID));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
<Page x:Class="SPWally.FunctionalPages.LookupOrders"
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:local="clr-namespace:SPWally.FunctionalPages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="LookupOrders">
<Grid Background="AliceBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30" Text="Search For Order" />
<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,335,0" Text="OrderID: " />
<TextBox Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Width="300" Margin="0,0,20,0" Text="{Binding Path=OrderIDSearch, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
<Button Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Width="40" Height="20" Margin="10,0,0,0" Content="Find" Click="Find_Click" />
<Button Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Width="50" Height="20" Margin="70,0,0,0" Content="Refund" Click="Refund_Click" />
<TextBlock Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,100,0" Text="OrderID:"/>
<TextBlock Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,100,0" Text="Customer:"/>
<TextBlock Grid.Row="5" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,100,0" Text="Product:"/>
<TextBlock Grid.Row="6" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,100,0" Text="Branch:"/>
<TextBlock Grid.Row="7" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,100,0" Text="Sales Price:"/>
<TextBlock Grid.Row="8" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,100,0" Text="Quantity:"/>
<TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Margin="30,0,0,0" Text="{Binding Path=Order.OrderID, Mode=TwoWay}"/>
<TextBlock Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Margin="30,0,0,0" Text="{Binding Path=Order.Customer.FullName, Mode=TwoWay}"/>
<TextBlock Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Margin="30,0,0,0" Text="{Binding Path=Order.Product.ProductName, Mode=TwoWay}"/>
<TextBlock Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Margin="30,0,0,0" Text="{Binding Path=Order.Branch.BranchName, Mode=TwoWay}"/>
<TextBlock Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Margin="30,0,0,0" Text="{Binding Path=Order.SalesPrice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Margin="30,0,0,0" Text="{Binding Path=Order.Stocks, Mode=TwoWay}"/>
<Button Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="60" Height="25" Content="Cancel" Command="{x:Static NavigationCommands.BrowseBack}" />
</Grid>
</Page>
Keep in mind that I've literally learned everything I know about data binding in the last 48 hours so bear with me here.
Any kind of help at all is very much appreciated. Thank!
You need to implement INotifyPropertyChanged in Orders class.
You need to Invoke OnPropertyChanged() inside all setters.
Where your view model is getting instantiated ? I guess since you are recreating your viewmodel instance in ModifyOrder class
boundData = new ViewModel();
binding is lost and may be because of that its not showing up .
...Yeah, so. It was the
public ViewModel()
{
Order = new Order();
}
that was wiping clean the private static Orders _Order in the ViewModel class every time it was instantiated so I changed it to
public ViewModel()
{
if (_Order == null)
{
Order = new Order();
}
}
and that fixed the problem...
Thanks to everyone that helped me out with this! I learned a lot from you all!
I'm going to go cry myself to sleep now <3
I have done opening the modal dialog in Wpf MVVM. But I am new to prism. I have tried the following code in MVVM. Now its working fine. But I want to do the same concept using prism with ribbon window. In the below code I have not used ribbon window, instead of ribbon window I used the button. please refer the below code,
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vm:ModalDialogViewModel}">
<view:ModalDialog />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
<Grid>
<Button
Width="150"
Height="25"
Margin="0,10,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding Path=LoadViewCommand}"
Content="Show Modal Dialog" />
</Grid>
</Grid>
<Grid Visibility="{Binding IsCloseModalWindow, Converter={StaticResource BooleanToVisibilityConverter}}">
<Border Background="#90000000">
<Border
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="White"
BorderBrush="Transparent"
BorderThickness="0"
CornerRadius="0">
<Border.BitmapEffect>
<DropShadowBitmapEffect
Direction="270"
Opacity="0.5"
ShadowDepth="0.7"
Color="Black" />
</Border.BitmapEffect>
<ContentControl Content="{Binding Path=CurrentViewModel}" />
</Border>
</Border>
</Grid>
</Grid>
MainWindowViewModel
public class MainWindowViewModel : ViewModelBase, ICloseWindow
{
private ICommand loadViewCommand;
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
this.OnPropertyChanged("CurrentViewModel");
}
}
private bool isCloseModalWindow = false;
public bool IsCloseModalWindow
{
get { return isCloseModalWindow; }
set { isCloseModalWindow = value; OnPropertyChanged("IsCloseModalWindow"); }
}
public MainWindowViewModel()
{
}
public ICommand LoadViewCommand => loadViewCommand ?? (loadViewCommand = new RelayCommand(showView, canShowView));
private void showView(object obj)
{
IsCloseModalWindow = true;
CurrentViewModel = new ModalDialogViewModel(new ModalDialogModel() { Name = "New Modal Window" }, this);
}
private bool canShowView(object obj)
{
return true;
}
public void closeWindow()
{
IsCloseModalWindow = false;
}
}
ModalDialog
<Grid MinWidth="300" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid
Grid.Row="0"
MinHeight="30"
Background="SkyBlue">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Width="40"
Height="25"
Content="X"
BorderThickness="0"
Background="Transparent"
Foreground="White"
Margin="0,0,5,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding CancelCommand}"
CommandParameter="{Binding ElementName=modalDialog}"
ToolTip="Close" />
</Grid>
</Grid>
<Grid Grid.Row="1" Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0"
Grid.Column="0"
Content="Name"
Style="{StaticResource commonMargin}" />
<Label
Grid.Row="0"
Grid.Column="1"
Content=":"
Style="{StaticResource commonMargin}" />
<TextBox
Grid.Row="0"
Grid.Column="2"
Width="100"
Text="{Binding Path=Name}"
Style="{StaticResource commonTimerMargin}" />
</Grid>
<Grid
Grid.Row="2"
Margin="0,0,15,10"
HorizontalAlignment="Right">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="0"
Width="80"
Height="30"
Margin="0,0,10,0"
Background="White"
Command="{Binding CancelCommand}"
CommandParameter="{Binding ElementName=modalDialog}"
Content="CANCEL" />
<Button
Grid.Column="1"
Width="80"
Height="30"
Background="SkyBlue"
Command="{Binding ApplyCommand}"
CommandParameter="{Binding ElementName=modalDialog}"
Content="APPLY"
Foreground="White" />
</Grid>
</Grid>
ModalDialogViewModel
public class ModalDialogViewModel : ViewModelBase
{
private ICommand cancelCommand;
public ModalDialogModel Model { get; private set; }
private ICloseWindow _closeWindow;
public ModalDialogViewModel(ModalDialogModel modalDialogModel, ICloseWindow closeWindow)
{
this.Model = modalDialogModel;
this._closeWindow = closeWindow;
}
public ICommand CancelCommand => cancelCommand ?? (cancelCommand = new RelayCommand(CloseWindow, CanCloseWindow));
private void CloseWindow(object obj)
{
_closeWindow.closeWindow();
}
private bool CanCloseWindow(object obj)
{
return true;
}
}
My Requirement is when user click the ribbon window button the modal dialog is open. The Shell window is a ribbon window. I have added HomeTab module and many other modules as separate class library.
You can do it like this in .xaml:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:prism="http://prismlibrary.com/"
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding PopUpDialogActionBinding}"> '<-- Here is your action binding
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="False">
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="Window">
<Setter Property="Icon" Value="IconPath"/>
<Setter Property="Height" Value="400"/>
<Setter Property="Width" Value="400"/>
</Style>
</prism:PopupWindowAction.WindowStyle>
<prism:PopupWindowAction.WindowContent>
<views:YourCustomView /> ' <--- Put your view into the dialog
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Grid>
...
</Grid>
I did it as in the example 28:
https://github.com/PrismLibrary/Prism-Samples-Wpf
I'm working on my first UWP app and I want create a UI like this . For each list item (project) there'll be a set of buttons. For certain list items(projects) some of these buttons will be disabled some times. So I need to disable and change the image for such button in those list items(projects).
I tried to implement it using a list view like this. But I am not sure how I can enable/disable some of those buttons depending on the condition.
Tried adding a DataContextChanged event and trying to access the buttons there. But not sure how I can access those buttons.
Please let me know whether the following approach is correct or is there a better way to do what I am trying to achieve in the above image.
<ListView x:Name="stepsListView" Margin="10,0,0,0" RequestedTheme="Dark" FontSize="24" Background="{StaticResource procedure_app_white}" Foreground="Black" BorderThickness="1.5" BorderBrush="Transparent" ItemsSource="{Binding projectList}" HorizontalAlignment="Left">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<!-- Item -->
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="#c0c0c0">
<Grid Width="auto" HorizontalAlignment="Stretch" DataContextChanged="Grid_DataContextChanged" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" FontSize="30" Grid.Row="0" Grid.ColumnSpan="7" Text="{Binding projectName}" Foreground="{StaticResource procedure_app_orange_text }" />
<Button x:Name="warningButton" Width="40" Height="40" Grid.Column="1" Grid.Row="1" Tag="{Binding projectId}" Click="warningButtonClick" Foreground="{StaticResource procedure_app_orange_text }">
<Button.Background>
<ImageBrush ImageSource="Asset/step_ncwr.png">
</ImageBrush>
</Button.Background>
</Button>
<Button x:Name="commentButton" Width="40" Height="40" Grid.Column="2" Grid.Row="1" Tag="{Binding projectId}" Click="CommentButtonClick" Foreground="{StaticResource procedure_app_orange_text }" IsTapEnabled="True">
<Button.Background>
<ImageBrush ImageSource="Asset/step_comment.png">
</ImageBrush>
</Button.Background>
</Button>
<Button x:Name="imageButton" Width="40" Height="40" Grid.Column="3" Grid.Row="1" Tag="{Binding projectId}" Click="ImageButtonClick" Foreground="{StaticResource procedure_app_orange_text }">
<Button.Background>
<ImageBrush ImageSource="Asset/step_image.png">
</ImageBrush>
</Button.Background>
</Button>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
The answer is to variable dependent on what structure you've gone with so I am going to make some assumptions and roll with it.
First I am going to assume your ViewModel has an ObservableCollection called ProjectList and that this ProjectList is made up of ProjectModel's
ProjectModel.cs
public class ProjectModel : INotifyPropertyChanged{
private bool _isNcwrEnabled;
public bool IsNcwrEnabled{
get{ return _isNcwrEnabled; }
set{ _isNcwrEnabled = value; OnPropertyChanged("IsNcwrEnabled"); }
}
private bool _isCommentEnabled;
public bool IsCommentEnabled{
get{ return _isCommentEnabled; }
set{ _isCommentEnabled= value; OnPropertyChanged("IsCommentEnabled"); }
}
private bool _isImageEnabled;
public bool IsImageEnabled{
get{ return _isImageEnabled; }
set{ _isImageEnabled= value; OnPropertyChanged("IsImageEnabled"); }
}
public void OnPropertyChanged(String prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
In your ViewModel you should have
ObservableCollection<ProjectModel> ProjectList {get; private set; }
Finally in your View
<Button IsEnabled="{Binding IsNcwrEnabled}" x:Name="warningButton" Width="40" Height="40" Grid.Column="1"
Grid.Row="1" Tag="{Binding projectId}" Click="warningButtonClick"
Foreground="{StaticResource procedure_app_orange_text }">
<Button.Background>
<ImageBrush ImageSource="Asset/step_ncwr.png"/>
</Button.Background>
</Button>
<Button IsEnabled="{Binding IsCommentEnabled}" x:Name="commentButton" Width="40" Height="40" Grid.Column="2"
Grid.Row="1" Tag="{Binding projectId}" Click="CommentButtonClick"
Foreground="{StaticResource procedure_app_orange_text }" IsTapEnabled="True">
<Button.Background>
<ImageBrush ImageSource="Asset/step_comment.png"/>
</Button.Background>
</Button>
<Button IsEnabled="{Binding IsImageEnabled}" x:Name="imageButton" Width="40" Height="40" Grid.Column="3"
Grid.Row="1" Tag="{Binding projectId}" Click="ImageButtonClick"
Foreground="{StaticResource procedure_app_orange_text }">
<Button.Background>
<ImageBrush ImageSource="Asset/step_image.png"/>
</Button.Background>
</Button>
Summary of Changes
The models in the collection that your ListView is bound to needs to contain enabled properties for your Buttons to bind to
In your View, bind your Buttons to your new properties
I'm searching for hours now but could not find the correct way how to do that.
I build a UserControl "MyToolbarGroup" having a GroupText and an empty Stackpanel inside.
Now I want to use the control MyToolbarGroup on my other UserControl "MyUserControl" and create some Buttons inside the Stackpanel of the MyToolbarGroup control.
MyToolbarGroup-XAML
<UserControl x:Class="TestUi.MyToolbarGroup"
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:DesignWidth="153" d:DesignHeight="103">
<Grid>
<Border BorderBrush="Black" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5,5,5,5" />
<Label Grid.Row="1" HorizontalContentAlignment="Center" Content="{Binding Path=GroupText}" Background="LightBlue" />
</Grid>
</Border>
</Grid>
</UserControl>
MyToolbarGroup-Code
public partial class MyToolbarGroup : UserControl
{
public static readonly DependencyProperty GroupTextProperty = DependencyProperty.Register("GroupText", typeof(string), typeof(MyToolbarGroup));
public String GroupText
{
get { return (String)GetValue(GroupTextProperty); }
set { SetValue(GroupTextProperty, value); }
}
public MyToolbarGroup()
{
InitializeComponent();
DataContext = this;
}
}
MyUserControl-XAML
<UserControl
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:local="clr-namespace:TestUi" x:Class="TestUi.MyUserControl"
mc:Ignorable="d"
d:DesignHeight="224" d:DesignWidth="343">
<Grid>
<local:MyToolbarGroup HorizontalAlignment="Left" Margin="40,30,0,0" VerticalAlignment="Top" Height="148" Width="238" GroupText="Test-Group">
<!-- Something like that
<Stackpanel-Inside-MyToolbarGroup>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Height="65" Width="65" Content="Button 1"/>
</Stackpanel-Inside-MyToolbarGroup>
-->
</local:MyToolbarGroup>
</Grid>
</UserControl>
Any ideas how to set some buttons inside the stackpanel?
Thanx for any help!
include a ContentPresenter in MyToolbarGroup template to accept user Content
<UserControl x:Class="TestUi.MyToolbarGroup"
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:testUi="clr-namespace:TestUi"
mc:Ignorable="d" d:DesignWidth="153" d:DesignHeight="103">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid>
<Border BorderBrush="Black" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<ContentPresenter Content="{TemplateBinding Content}"/>
<Label Grid.Row="1" HorizontalContentAlignment="Center"
Content="{Binding GroupText, RelativeSource={RelativeSource AncestorType=testUi:MyToolbarGroup}}"
Background="LightBlue" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
and then use it like this:
<testUi:MyToolbarGroup Grid.Row="2"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Height="148" Width="238" GroupText="Test-Group">
<!--any content, e.g. -->
<StackPanel Orientation="Horizontal">
<TextBlock Text="A" Margin="5"/>
<TextBlock Text="B" Margin="5"/>
</StackPanel>
</testUi:MyToolbarGroup>
result:
A stack panel doesn't have the functionality you want (if I understand you correctly). What you want is a dynamic collection of controls based on some data right?
What you need is an ItemsControl:
<ItemsControl ItemsSource="{Binding MyButtonDataCollection}" >
<ItemsControl.ItemTemplate >
<DataTemplate >
<Button Content="{Binding ButtonName}"
Command="{Binding ButtonClick}"
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
bound to a collection of data objects that have the properties and commands you want to bind to:
class MyButtonData : INotifyPropertyChanged
{
String ButtonName { get; set; } //notify property changed and all that
public ICommand ButtonClick
{
get;
internal set;
}
private bool CanExecuteButtonClick()
{
//can this execute?
return true;
}
private void CreateButtonClick()
{
ButtonClick = new RelayCommand(SaveExecute, CanExecuteSaveCommand);
}
public void ButtonClickExecute()
{
//do my logic for click
}
}
If you want layout inside MyToolbarGroup to be predefined, you need to replace StackPanel with ItemsControl having StackPanel as ItemsPanel.
Here's MyToolbarGroup XAML:
<UserControl x:Class="WpfApplication2.MyToolbarGroup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2">
<Border BorderBrush="Black" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" HorizontalAlignment="Center" Margin="5,5,5,5"
ItemsSource="{Binding GroupItems, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Label Grid.Row="1" HorizontalContentAlignment="Center"
Content="{Binding GroupText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
Background="LightBlue" />
</Grid>
</Border>
</UserControl>
and code-behind:
public partial class MyToolbarGroup : UserControl
{
public MyToolbarGroup()
{
InitializeComponent();
}
public string GroupText
{
get { return (string)GetValue(GroupTextProperty); }
set { SetValue(GroupTextProperty, value); }
}
public static readonly DependencyProperty GroupTextProperty =
DependencyProperty.Register("GroupText", typeof(string), typeof(MyToolbarGroup), new PropertyMetadata(null));
public IEnumerable GroupItems
{
get { return (IEnumerable)GetValue(GroupItemsProperty); }
set { SetValue(GroupItemsProperty, value); }
}
public static readonly DependencyProperty GroupItemsProperty =
DependencyProperty.Register("GroupItems", typeof(IEnumerable), typeof(MyToolbarGroup), new PropertyMetadata(null));
}
Now, you can use it like this:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow">
<Grid>
<local:MyToolbarGroup HorizontalAlignment="Left" Margin="40,30,0,0" VerticalAlignment="Top" Height="148" Width="238" GroupText="Test-Group">
<local:MyToolbarGroup.GroupItems>
<x:Array Type="{x:Type sys:Object}">
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Height="65" Width="65" Content="Button 1"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Height="65" Width="65" Content="Button 2"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Height="65" Width="65" Content="Button 3"/>
</x:Array>
</local:MyToolbarGroup.GroupItems>
</local:MyToolbarGroup>
</Grid>
</Window>
Screenshot of result: