WPF - Show/Hide Selected Column With Right Click - c#

I want to make Show and Hide control for my DataGrid on WPF. I just want to add 2 button (with ContextMenu or something) but i don't know how can i do it.
I just want to select a column from my DataGrid and when i click "Hide" button, it will be hide. When i click "Show" button and it will be show again. Can you help me for this? Thank you.
This is my DataGrid Code;
gridview1.CanUserDeleteRows = false;
gridview1.IsReadOnly = true;
SQLiteConnection baglanti = new SQLiteConnection("Data Source=db/veritabani.s3db");
baglanti.Open();
string komut = "SELECT * FROM belgeler";
SQLiteDataAdapter da = new SQLiteDataAdapter(komut, baglanti);
DataTable dt = new DataTable();
da.Fill(dt);
gridview1.ItemsSource = dt.DefaultView;

First of all I'm not really sure how you could right click a column to show it again if it's hidden.
Apart from that you could try something like this for your ContextStripMenu but I'm away from my PC so I can't be sure it works:
private void dataGridView1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ContextMenu contextMenu = new ContextMenu();
contextMenu.MenuItems.Add(new MenuItem("Hide"));
contextMenu.MenuItems.Add(new MenuItem("Show"));
contextMenu.ItemClicked += new ToolStripItemClickedEventHandler(contexMenu_ItemClicked);
contextMenu.Show(dataGridView1, new Point(e.X, e.Y));
}
}
And this for your event handler:
void contexMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
int mouseOverColumn = dataGridView1.HitTest(e.X,e.Y).ColumnIndex;
dataGridView1.Columns[mouseOverColumn].Visible = false;
}

You could do something like this
Note: This is far from perfect, but you will get an idea of how you can solve your problem
If you struggle with something let me know, i try to help then
<DataGrid x:Name="dataGrid">
<DataGrid.Resources>
<local:VisibilityToBooleanConverter x:Key="VisibilityToBooleanConverter"></local:VisibilityToBooleanConverter>
<local:BindingProxy x:Key="BindingProxy" Data="{Binding ElementName=dataGrid, Path=Columns}"></local:BindingProxy>
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu ItemsSource="{Binding Source={StaticResource BindingProxy}, Path=Data}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="IsCheckable" Value="True"/>
<Setter Property="IsChecked" Value="{Binding Visibility,Converter={StaticResource VisibilityToBooleanConverter}}"/>
<Setter Property="Header" Value="{Binding Header}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
...
</DataGrid>
public class VisibilityToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility visibility)
{
return visibility == Visibility.Visible;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return (boolValue) ? Visibility.Visible : Visibility.Hidden;
}
return Visibility.Hidden;
}
}

Related

Display deleted rows in a DataTable

So I have a DataTable that I bind to the DataGrid in XAML.
User is allowed to add, modify and remove rows for a table. I would like to mark rows with a specific colour, depending on the action that user makes. For instance, if user adds a row, that row will be marked as Green. If user modifies a row, that row will then be marked as orange. And if user removes the row, that row will be marked as red.
The problem that I have is that the removed row is no longer visible once I call row.Delete(); from a view model.
Is there a way to keep a DataRow marked for removal shown in a DataGrid? I know how to achieve the row background effect, depending on user action. The only problem is I don't know how to keep the deleted row visible. The idea is that user can either revert changes or apply them and that's when the pending deletion row should be actually deleted.
EDIT (Added example on how I update row background colour):
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=Row.RowState}" Value="{x:Static data:DataRowState.Deleted}" />
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsSelected}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="IndianRed" TargetName="DGR_Border"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontWeight" Value="Bold"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
I think, when user marks row for deleting - you should save it index somewhere (int[] array or List<int> for example), then call yourDataGridView.Rows.RemoveAt(index) for each element in that collection when user finished working with table.
Maybe something like:
//Collection for rows indexes which will be deleted later
List<int> rowsToDelete = new List<int>();
//Call this when user want to delete element
private void MarkElementsToRemove()
{
if (yourDataGrid.SelectedItems.Count > 0)
{
//Get selected by user rows
for (int i = 0; i < yourDataGrid.SelectedItems.Count; ++i)
{
DataGridRow row = (DataGridRow)yourDataGrid.SelectedItems[i];
//Fill rows background with some color to mark them visually as "deleted"
row.Background = new SolidColorBrush(Color.FromRgb(255, 0, 0));
//Get row index to remove it later and add it to collection
rowsToDelete.Add(row.GetIndex());
}
}
}
// Call this when user finished work with DataGrid and items may be removed
private void RemoveMarkedElements()
{
foreach (int index in rowsToDelete)
{
yourDataGrid.Items.RemoveAt(index);
}
rowsToDelete.Clear();
}
Instead of index you may save whole DataGridRow and call yourDataGrid.Remove(wholeRow);.
And for reverse deletion, you just unmark it by removing color and removing row index or whole row from a collection.
If I understood you correctly, you need to use the Delete key not to delete lines, but to put a marker on them.
And in the DataGrid, you need to highlight color the rows marked with this marker.
You have not shown your table, so I will demonstrate in my simple conciliation.
The example uses the BaseInpc and RelayCommand classes.
In addition to them, the command extension method is used:
using System.Windows.Input;
namespace Simplified
{
public static class CommandExtensionMethods
{
public static bool TryExecute(this ICommand command, object parameter)
{
bool can = command.CanExecute(parameter);
if (can)
command.Execute(parameter);
return can;
}
public static bool TryExecute(this ICommand command)
=> TryExecute(command, null);
}
}
ViewModel:
using Simplified;
using System.Data;
using System.Windows.Input;
namespace DeferredRowDeletion
{
public class DrdViewModel : BaseInpc
{
public DataTable Table { get; } = new DataTable();
public DrdViewModel()
{
Table.Columns.Add("Name", typeof(string));
Table.Columns.Add("Value", typeof(int));
Table.Columns.Add("Marked for deletion", typeof(bool));
foreach (var name in new string[] { "First", "Second", "Third", "Fourth", "Fifth" })
{
var row = Table.NewRow();
row[0] = name;
row[1] = Table.Rows.Count;
row[2] = Table.Rows.Count % 2 == 1;
Table.Rows.Add(row);
}
}
private ICommand _markRemoveChangeCommand;
private bool _isRemoveRowsImmediately;
public ICommand MarkRemoveChangeCommand => _markRemoveChangeCommand
?? (_markRemoveChangeCommand = new RelayCommand<DataRow>(
row => row[2] = !(bool)(row[2] ?? false),
row => !IsRemoveRowsImmediately
));
public bool IsRemoveRowsImmediately
{
get => _isRemoveRowsImmediately;
set => Set(ref _isRemoveRowsImmediately, value);
}
}
}
Window XAML:
<Window x:Class="DeferredRowDeletion.DrdWindow"
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:DeferredRowDeletion"
mc:Ignorable="d"
Title="DrdWindow" Height="450" Width="800">
<FrameworkElement.DataContext>
<local:DrdViewModel/>
</FrameworkElement.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox Content="Removw Rows Immediately"
IsChecked="{Binding IsRemoveRowsImmediately}"
Margin="5"/>
<DataGrid x:Name="dataGrid" Grid.Row="1"
ItemsSource="{Binding Table, Mode=OneWay}"
AutoGeneratingColumn="OnAutoGeneratingColumn"
CanUserDeleteRows="{Binding IsRemoveRowsImmediately}"
PreviewKeyDown="OnPreviewKeyDown">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding [Marked for deletion]}" Value="true">
<Setter Property="Background" Value="HotPink"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</Grid>
</Window>
Code Behind Window:
using Simplified;
using System.Data;
using System.Windows;
using System.Windows.Input;
namespace DeferredRowDeletion
{
public partial class DrdWindow : Window
{
public DrdWindow()
{
InitializeComponent();
}
private void OnAutoGeneratingColumn(object sender, System.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyName == "Marked for deletion")
e.Cancel = true;
}
private void OnPreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
DrdViewModel viewModel = (DrdViewModel)DataContext;
var rowView = dataGrid.CurrentItem as DataRowView;
if (rowView != null && !rowView.IsEdit)
viewModel.MarkRemoveChangeCommand.TryExecute(rowView.Row);
}
}
}
}
If you are unable to use this example, then write the reason and add details to the explanation of your question.
The answer is supplemented by clarifications for the added details:
I think I should've mentioned that I use DataRow's RowState property bound to the DataTrigger to update row background colour. Added details to the question.
To control the visibility of rows, you need to change the value of the DataTable.DefaultView.RowStateFilter property.
This is not hard to do in the ViewModel.
But an additional problem is that the RowState property does not notify about its change.
So the trigger binding won't work just like that.
In my example, I solved this by calling Items.Refresh ().
Perhaps you are using a different solution since you have not written about any problems associated with this.
using Simplified;
using System.Data;
using System.Windows.Input;
namespace DeferredRowDeletion
{
public class ShowDeletedRowsViewModel : BaseInpc
{
public DataTable Table { get; } = new DataTable();
public ShowDeletedRowsViewModel()
{
Table.Columns.Add("Name", typeof(string));
Table.Columns.Add("Value", typeof(int));
foreach (var name in new string[] { "First", "Second", "Third", "Fourth", "Fifth" })
{
var row = Table.NewRow();
row[0] = name;
row[1] = Table.Rows.Count;
Table.Rows.Add(row);
}
// Commits all the changes
Table.AcceptChanges();
Table.Rows[1].Delete();
Table.Rows[3].Delete();
// Show Deleded Rows
IsVisibilityDelededRows = true;
}
private ICommand _markRemoveChangeCommand;
private bool _isVisibilityDelededRows;
public ICommand MarkRemoveChangeCommand => _markRemoveChangeCommand
?? (_markRemoveChangeCommand = new RelayCommand<DataRow>(
row => IsVisibilityDelededRows ^= true,
row => !IsVisibilityDelededRows
));
public bool IsVisibilityDelededRows
{
get => _isVisibilityDelededRows;
set => Set(ref _isVisibilityDelededRows, value);
}
protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue)
{
base.OnPropertyChanged(propertyName, oldValue, newValue);
if (propertyName == nameof(IsVisibilityDelededRows))
{
// Change the row filter if the associated property has changed
if (IsVisibilityDelededRows)
{
Table.DefaultView.RowStateFilter |= DataViewRowState.Deleted;
}
else
{
Table.DefaultView.RowStateFilter &= ~DataViewRowState.Deleted;
}
}
}
}
}
<Window x:Class="DeferredRowDeletion.SdrWindow"
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:DeferredRowDeletion" xmlns:data="clr-namespace:System.Data;assembly=System.Data"
mc:Ignorable="d"
Title="SdrWindow" Height="450" Width="800">
<FrameworkElement.DataContext>
<local:ShowDeletedRowsViewModel/>
</FrameworkElement.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<CheckBox x:Name="cbAutoRefresh" Content="Auto Items.Refresh()" IsChecked="True" Margin="5"/>
<CheckBox Content="Visibility Deleded Rows"
IsChecked="{Binding IsVisibilityDelededRows}"
Margin="5"/>
</StackPanel>
<DataGrid x:Name="dataGrid" Grid.Row="1"
ItemsSource="{Binding Table, Mode=OneWay}"
PreviewKeyUp="OnPreviewKeyUp">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Row.RowState, Mode=OneWay}"
Header="RowState"/>
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Row.RowState}" Value="{x:Static data:DataRowState.Deleted}">
<Setter Property="Background" Value="HotPink"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</Grid>
</Window>
private void OnPreviewKeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete && cbAutoRefresh.IsChecked == true)
dataGrid.Items.Refresh();
}

Changing foreground color binding of cells based on condition WPF

Im trying to change the default color of a cell to red based on if the value is over a set limit. Currently, what is happening is that I have a grid with a list of oils with a checkbox. When a checkbox for any particular oil is checked, that oil is added to another grid where I can manipulate its amount. For example, if I check 1 oil, its "UsagePercentage" becomes 100. If I check another, the percentages are updated to 50/50. From there, if I click a button to increment its value by 1, it will change to 66.6/33.3 and so on.
What Im trying to implement based on my code is that if the oil's UsagePercentage is more than the UsageLimit, change the foreground text to red, otherwise keep it LightGray. But when I run the program, the UsagePercentage text is black. I dont't understand why binding UsagePercentage works, but ForegroundColor doesn't.
Oil.cs
public float UsagePercentage
{
get { return mUsagePercentage; }
set
{
mUsagePercentage = value;
NotifyPropertyChanged("UsagePercentage");
}
}
OilList.cs
public void UpdateFormulaWeight()
{
float ftw = mOils.Sum(oil => oil.TotalWeight);
mFormulaTotalWeight = ftw;
NotifyPropertyChanged("UsagePercentage");
foreach (Oil oil in mOils)
{
oil.UsagePercentage = (oil.TotalWeight / mFormulaTotalWeight) * 100f;
if (oil.UsagePercentage > oil.UsageLimit)
ForegroundColor = "Red";
else
ForegroundColor = "LightGray";
NotifyPropertyChanged("ForegroundColor");
NotifyPropertyChanged("UsagePercentage");
}
}
private string ForegroundColor
{
get { return mForegroundColor; }
set
{
mForegroundColor = value;
NotifyPropertyChanged("ForegroundColor");
}
}
GrayToRed.cs
public class GrayToRed : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string input = value as string;
switch(input)
{
case "Red":
return Brushes.Red;
case "LightGray":
return Brushes.LightGray;
default:
return DependencyProperty.UnsetValue;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
MainWindow.xaml
<Window.Resources>
<converters:GrayToRed x:Key="GrayToRed" />
</Window.Resources>
...
<!-- USAGE PERCENTAGE -->
<DataGridTextColumn Header="Formula %" Binding="{Binding UsagePercentage, StringFormat={}{0:F2}%}" IsReadOnly="True" Width="64">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="#1e1e1e"/>
<Setter Property="Foreground" Value="{Binding ForegroundColor, Converter={StaticResource GrayToRed}}"/>
</Style>
</DataGridTextColumn.CellStyle>
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextBlock.VerticalAlignment" Value="Center" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

WPF binding dynamically created RadioButtons from code

I have stackpanel to which I am adding RadioButtons dynamically as children.
Radiobuttons have content which is integer.
I also have Y x Y grid (size determined from code dynamically), to which I'm adding dynamically Buttons and allow user to change Button's content into string which represents integer number.
Here is where I need help:
After checking arbitrary radiobutton from stackpanel, I'd like all buttons from grid that have same number to have their background color changed.
As I am new to WPF I am not sure how to achieve this and your help would be greatly appreciated.
EDIT:
I made a little progress, what I do is basically bind Button.Content with RadioButton.IsChecked and RadioButton.Content for everybutton and every radiobutton, but I have problem that it only works for last radiobutton here is code (rbuts=parent control of radiobuttons, MyGrid=parent control of buttons):
for (int z = 0; z < boardSize * boardSize; z++)
{
Button b1 = MyGrid.Children[z] as Button;
for (int i = 0; i < boardSize; i++)
{
MultiBinding rbtnBinding = new MultiBinding();
rbtnBinding.Converter = new RadioButtonHighlightConverter();
rbtnBinding.Bindings.Add(new Binding("IsChecked") { Source = rbuts.Children[i] });
rbtnBinding.Bindings.Add(new Binding("Content") { Source = rbuts.Children[i] });
rbtnBinding.Bindings.Add(new Binding("Content") { Source = MyGrid.Children[z] });
rbtnBinding.NotifyOnSourceUpdated = true;
b1.SetBinding(Button.BackgroundProperty, rbtnBinding);
}
}
Its as if I cannot set many different multibindings for same button...
You could place your Button Styles in a Resource Dictionary and bind the Style for the Button and use a Converter
ButtonStyles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ButtonStyle1" TargetType="Button">
<Setter Property="Background" Value="Green"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<Style x:Key="ButtonStyle2" TargetType="Button">
<Setter Property="Background" Value="Red"/>
<Setter Property="FontSize" Value="14"/>
</Style>
</ResourceDictionary>
Then for the Button that has this requirement you bind Style to the property of interest
<Button ...
Style="{Binding Path=MyDataProperty,
Converter={StaticResource ButtonStyleConverter}}"/>
And in the Converter you load the ButtonStyles Resource Dictionary and return the desired Style based on the value
public class ButtonStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Uri resourceLocater = new Uri("/YourNameSpace;component/ButtonStyles.xaml", System.UriKind.Relative);
ResourceDictionary resourceDictionary = (ResourceDictionary)Application.LoadComponent(resourceLocater);
if (value.ToString() == "Some Value")
{
return resourceDictionary["ButtonStyle1"] as Style;
}
return resourceDictionary["ButtonStyle2"] as Style;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
EDIT :
Added details for converter parameter
<RadioButton Content="None"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<RadioButton.IsChecked>
<Binding Path="MyProperty"
Converter="{StaticResource IntToBoolConverter}">
<Binding.ConverterParameter>
<sys:Int32>0</sys:Int32>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
</RadioButton>

WPF PRISM MVVM get selected item from grid control

I have a DevExpress Grid Control. I want to enable/disable a button based on the selected rows in the grid control, i.e., if any rows are selected in the grid control then this button should be enabled. Following is my GridControl code:
<dxg:GridControl x:Name="gridFloorplans" Grid.Column="1" Grid.Row="1" AutoGenerateColumns="None"
ItemsSource="{Binding FloorplanList.Result.View}"
SelectedItems="{Binding SelectedFloorplan,Mode=TwoWay}"
dx:ThemeManager.Theme="Default" SelectionMode="Row">
<dxg:GridControl.View>
<dxg:TableView AllowGrouping="False" ShowGroupPanel="False" AllowEditing="False" ShowDataNavigator="True" DataNavigatorButtons="Navigation" />
</dxg:GridControl.View>
<dxg:GridControl.Columns>
<dxg:GridColumn FieldName="Name" Header="Floorplan Name" Fixed="Left" />
<dxg:GridColumn FieldName="Season" Fixed="Left" />
<dxg:GridColumn FieldName="Version" Fixed="Left" />
</dxg:GridControl.Columns>
</dxg:GridControl>
Following is my ViewModel code:
private ObservableCollection<FloorplanData> _selectedFloorplan;
public FloorplanSearchViewModel(IErrorHandlerService inErrorHandler, INavigationService inNavigationService,
ISpaDataAdapter inDataAdapter, IAuthorizationService inAuthService)
{
// Set the commands
this.ShowStoreSetCommand = new DelegateCommand<IList<object>>(this.ShowStoreSet, this.CanShowStoreSet);
this.SearchFloorplansCommand = new DelegateCommand(this.SearchFloorplans);
this.ShowStatusChangeCommand = new DelegateCommand<IList<object>>(this.ShowStatusChange, this.CanShowStatusChange);
// Set up the default values for the search
this.StatusList = new List<object>();
this.StatusList.Add(Enum.GetName(typeof(FloorplanData.FloorplanStatus), FloorplanData.FloorplanStatus.Pending));
this.StatusList.Add(Enum.GetName(typeof(FloorplanData.FloorplanStatus), FloorplanData.FloorplanStatus.Review));
//Initiate the SelectedFloorplan property
//SelectedFloorplan = new ObservableCollection<FloorplanData>();
}
public ObservableCollection<FloorplanData> SelectedFloorplan
{
get
{
return _selectedFloorplan;
}
set
{
_selectedFloorplan = value;
this.ShowStatusChangeCommand.RaiseCanExecuteChanged();
}
}
public NotifyTaskCompletion<CollectionViewSource> FloorplanList
{
get;
private set;
}
private void ShowStatusChange(IList<object> inFloorplans)
{
try
{
// Create the navigation output
NavigationParameters args = new NavigationParameters();
args.Add(FloorplanStatusChangeViewModel.PARAM_FLOORPLAN_ID_LIST, GetFloorplanIdList(inFloorplans));
_navigationService.NavigateTo<Views.FloorplanStatusChangeView>(args);
}
catch (Exception ex)
{
_errorHandler.HandleError(ex);
}
}
private bool CanShowStatusChange(IList<object> inFloorplans)
{
// Check security to see if the current user is allowed to enter the status change screen
if (_authService.GetAccessLevel(1470) > AuthorizationLevel.None)
{
if (SelectedFloorplan!=null)
return true;
else
return false;
}
else
{
return false;
}
}
Following is the xaml code for the button:
<Button Margin="4,2" Content="Status Change" Command="{Binding ShowStatusChangeCommand}"
CommandParameter="{Binding SelectedItems, ElementName=gridFloorplans}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="ToolTip" Value="Open the Floorplan Status Change view for the selected floorplans" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="ToolTip" Value="You do not have access to open the Floorplan Status Change view" />
</Trigger>
<DataTrigger
Binding ="{Binding ElementName=gridFloorplans, Path=SelectedFloorplan}"
Value="-1">
<Setter Property="Button.IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
How can I enable/disable ShowStatus button based on whether any row is selected in the grid or not?
//You are not using `inFloorplans` parameter within your method body
//Need not pass this parameter
private bool CanShowStatusChange(IList<object> inFloorplans)
Same as CanShowStatusChange method create a property and bind it to the Button which you want to enable/disable
public bool CanShowStatusChange
{
get
{
if (_authService.GetAccessLevel(1470) > AuthorizationLevel.None)
{
if (SelectedFloorplan!=null)
return true;
else
return false;
}
else
{
return false;
}
}
}
Selected floor plan cant be an observable collection. The name detotes its a It denotes a single object. So
private FloorplanData _selectedFloorplan;
public FloorplanData SelectedFloorplan
{
get
{
return _selectedFloorplan;
}
set
{
_selectedFloorplan = value;
NotifyPropertyChanged("SelectedFloorplan");
//or its equivalent method to notify the change
NotifyPropertyChanged("CanShowStatusChange");
//or its equivalent method to notify the change of CanShowStatusChange.
}
}
Make sure you bind SelectedFloorplan property and CanShowStatusChange property in your UI so that they are updated.
1) IsEnabled will get bool value from CanShowStatusChange, so you dont need style.
2) You have grid`s selected items in VM, so why pass it via parameter?
<Button Margin="4,2" Content="Status Change" Command="{Binding ShowStatusChangeCommand}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="ToolTip" Value="Open the Floorplan Status Change view for the selected floorplans" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="ToolTip" Value="You do not have access to open the Floorplan Status Change view" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
3) You set SelectedFloorplan ones, then you just change items in collection! It means than we should subscribe on CollectionChanged
public FloorplanSearchViewModel(IErrorHandlerService inErrorHandler, INavigationService inNavigationService,
ISpaDataAdapter inDataAdapter, IAuthorizationService inAuthService)
{
// Set the commands
this.ShowStoreSetCommand = new DelegateCommand<IList<object>>(this.ShowStoreSet, this.CanShowStoreSet);
this.SearchFloorplansCommand = new DelegateCommand(this.SearchFloorplans);
this.ShowStatusChangeCommand = new DelegateCommand<IList<object>>(this.ShowStatusChange, this.CanShowStatusChange);
// Set up the default values for the search
this.StatusList = new List<object>();
this.StatusList.Add(Enum.GetName(typeof(FloorplanData.FloorplanStatus), FloorplanData.FloorplanStatus.Pending));
this.StatusList.Add(Enum.GetName(typeof(FloorplanData.FloorplanStatus), FloorplanData.FloorplanStatus.Review));
//Initiate the SelectedFloorplan property
SelectedFloorplan = new ObservableCollection<FloorplanData>();
SelectedFloorplan.CollectionChanged += SelectedFloorplanOnCollectionChanged;
}
private void SelectedFloorplanOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
this.ShowStatusChangeCommand.RaiseCanExecuteChanged();
}
public ObservableCollection<FloorplanData> SelectedFloorplan
{
get
{
return _selectedFloorplan;
}
set
{
_selectedFloorplan = value;
this.ShowStatusChangeCommand.RaiseCanExecuteChanged();
}
}
4) And
private bool CanShowStatusChange()
{
// Check security to see if the current user is allowed to enter the status change screen
if (_authService.GetAccessLevel(1470) > AuthorizationLevel.None)
{
if (SelectedFloorplan!=null && SelectedFloorplan.Any())
return true;
else
return false;
}
else
{
return false;
}
}

WPF Dependency Property Issue

I am wondering if anyone could explain me the difference between
binding a selected value of a Collection to a comboBox.
Or Binding the value to a Button Content.
Like that
<ComboBox x:Name="_culturedTitleViewModelSelector" Visibility="Hidden" Style="{StaticResource ResourceKey=_culturedTitleViewModelSelectorStyle}"
ItemsSource="{Binding Path=AvailableCultures, Source={x:Static Localized:ResourcesManager.Current}}"
SelectedValue="{Binding Path=CurrentCulture, Source={x:Static Localized:ResourcesManager.Current}}"
<Button x:Name="LanguageBtn" Content="{Binding Path=CurrentCulture, Source={x:StaticLocalized:ResourcesManager.Current}}"
The issue is If i Don't use the ComboBox up there, the DependencyProperty I Have in another class is not being called.
But if I Use the comboBox everything works...
Altought the comboBox doesnt do anything it's just a "workarround"
In my CS code when i CLick on my button I DO that :
ResourcesManager.Current.SwitchToNextCulture();
//We use a dummy comboBox to make sure the LanguageBehavior Property is being notified.
_culturedTitleViewModelSelector.SelectedItem = ResourcesManager.Current.CurrentCulture;
And if I Dont set the SelectedItem of the combobox to another culture. My languageBehavior class is not notified.
:
public class LanguageBehavior
{
public static DependencyProperty LanguageProperty =
DependencyProperty.RegisterAttached("Language",
typeof(string),
typeof(LanguageBehavior),
new UIPropertyMetadata(OnLanguageChanged));
public static void SetLanguage(FrameworkElement target, string value)
{
target.SetValue(LanguageProperty, value);
}
public static string GetLanguage(FrameworkElement target)
{
return (string)target.GetValue(LanguageProperty);
}
private static void OnLanguageChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
var element = target as FrameworkElement;
if (e.NewValue!=null)
element.Language = XmlLanguage.GetLanguage(e.NewValue.ToString());
}
}
I'd expect ComboBox Content to work the same as Button Content.
In my Generic.Xaml i do that :
<Style TargetType="{x:Type TextBlock}" x:Key="_textBlockLanguageProperty">
<Setter Property="WpfServices:LanguageBehavior.Language" Value="{Binding Path=CurrentCulture, Source={x:Static Localized:ResourcesManager.Current}}"
/>
</Style>
And that is CurrentCulture
public CultureInfo CurrentCulture
{
get { return CultureProvider.Current; }
set
{
if (value != CultureProvider.Current)
{
CultureProvider.Current = value;
OnCultureChanged();
}
}
}
Current :
public static ResourcesManager Current
{
get
{
if (_resourcesManager == null)
{
var cultureProvider = new BaseCultureProvider();
_resourcesManager = new ResourcesManager(cultureProvider);
_resourcesManager.Init();
}
return _resourcesManager;
}
}
EDIT :
My _culturedTitelViewModelSelectorStyle is
<Style TargetType="{x:Type ComboBox}" x:Key="_culturedTitleViewModelSelectorStyle">
<Setter Property="DisplayMemberPath" Value="DisplayName" />
<Setter Property="SelectedValuePath" Value="." />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="MaxHeight" Value="40" />
<Setter Property="FontSize" Value="20" />
<Setter Property="Margin" Value="5" />
<Setter Property="SelectedIndex" Value="0" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>
In the ComboBox you are binding the SelectedValue to a specific culture. This will select that culture from the list of available cultures, and therefor, trigger a set on the CurrentCulture property.
The Content property of a Button is merely displaying something to the user, it is not doing any assigning. It reads the property value and then displays it. That is why you need to manually change the Culture in the Click event to get it to do anything.
If you want the user to be able to select a value from a list of available values, a ComboBox or ListBox is the way to go. A Button is for triggering a specific action, not for selecting from a list.

Categories