SilverLight 4.0 C# - DomainDataSource DataGrid with CheckBox Column (UI only - not a data field) to allow user to select multiple records/rows - entity-framework

New to SilverLight and to posting here. Please have mercy and be specific :)
Using RIA services with DomainDataSource and DataGrid control to display data rows from SQL server query
Goal: Have checkbox column (UI only - not a data field) to allow user to select multiple records/rows
BackGround:
1) Created new SilverLight 4, C# solution with RIA services
2) in ProjectName.Web
Created Entity Framework (EF) model referencing SQL server table/view (built solution).
Created Domain Sevice using EF model (built solution).
3) in SilverLightProjectName
From Data Sources window, dragged table onto a design surface to create DomainDataSource and DataGrid (this works great to bind DataGrid to data source)
4) in MainPage.XAML added checkbox column
What's Happening: checkboxes are selected/checked by user, scroll down, scroll back up, all checkboxes reset and ONLY Datagrid.SelectedItem is still checked. I have read this behavior is 'by design' due to paging.
<sdk:DataGrid RowStyle="{StaticResource newDataGridStyle}" AutoGenerateColumns="False" ItemsSource="{Binding ElementName=ddsPagerApp, Path=Data}" Name="vwPagerAppDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" MouseLeftButtonDown="vwPagerAppDataGrid_MouseLeftButtonDown" VerticalGridLinesBrush="#FFB9B9B9" FontSize="10" Grid.Row="3" SelectionChanged="vwPagerAppDataGrid_SelectionChanged" KeyDown="vwPagerAppDataGrid_KeyDown" MouseLeftButtonUp="vwPagerAppDataGrid_MouseLeftButtonUp" MouseRightButtonUp="vwPagerAppDataGrid_MouseRightButtonUp" DataContext="{Binding}" SelectionMode="Single" IsEnabled="True" IsReadOnly="False" TabIndex="2" Grid.Column="1" Margin="0,10,9,9">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn IsReadOnly="False">
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox Name="ChkSelected" IsThreeState="False" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" Unchecked="IndividualCheckBox_Unchecked" Checked="IndividualCheckBox_Checked" Width="Auto" Height="Auto" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTextColumn x:Name="fullNameColumn" Binding="{Binding Path=FullName}" Header="Full Name" IsReadOnly="True" />
<sdk:DataGridTextColumn x:Name="departmentColumn" Binding="{Binding Path=department}" Header="Department" IsReadOnly="True" />
<sdk:DataGridTextColumn x:Name="pager_number_displayColumn" Binding="{Binding Path=pager_number_display}" Header="Pager Number" IsReadOnly="True" />
<sdk:DataGridTextColumn x:Name="PageTo" Binding="{Binding Path=PageTo}" Header="Page To" IsReadOnly="True" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:vwPagerApp, CreateList=true}" Height="0" LoadedData="ddsPagerApp_LoadedData" Name="ddsPagerApp" QueryName="GetVwPagerAppsQuery" Width="0" Margin="10,0,25,45" Background="#FF7D0000" Foreground="#FF7D0000" Visibility="Visible">
<riaControls:DomainDataSource.DomainContext>
<my:NotifyDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
Attempt 1:
in EFModel.edmx, added Boolean Scalar Property 'IsChecked'
in DomainService.metadata.cs, added public bool IsChecked { get; set; }
in MainPage.XAML. added (above) IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"
Getting error: Error 11009: Property ' ' is not mapped
UPDATE: Reversed Attempt 1:
Attempt 2:
Researching possibility of defining a partial class for the entity, wiring to DataGrid, and using that to track CheckBox values. Any advice on if this will work/how to?
Trying my best to absorb this. Please enlighten me...and thank you in advance :)

This is what I implemented and it worked beautifully. I hope it helps someone else in the future :)
1) SilverLight Project C#: Extend entity class using partial class (in separate file, but within same namespace – compiles into 1 class but keeps generated code separate)
namespace Pager.Web
{
public partial class pager_grp
{
bool _IsChecked = false;
public bool IsChecked
{
get
{return this._IsChecked;}
set
{this._IsChecked = !IsChecked;}
}
}
}
2) SilverLight Project GUI: Create Column in DataGrid of type DataGridTemplateColumn named chkSelected
3) SilverLight Project XAML: use template and bind to new property declared in partial class
<sdk:DataGridTemplateColumn IsReadOnly="False">
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox Name="ChkSelected" IsThreeState="False" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" Unchecked="IndividualCheckBox_Unchecked" Checked="IndividualCheckBox_Checked" Width="Auto" Height="Auto" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>

Related

Control events inside interaction pop up not working

I have an interaction request pop up and inside that pop up, I have few controls like time picker, radcombobox and so on. I want change events of these controls hit in my view model class using prism commands.
my sample code is not working, time picket value change is not hitting on OnReasonTimeChanged Method.
XAML file
<i:Interaction.Triggers>
<i:EventTrigger SourceName=" " EventName="ValueChanged">
<i:InvokeCommandAction Command="{Binding ReasonTimeChanged}" />
</i:EventTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding SummaryConfirmationInteractionRequest}">
<prism:PopupChildWindowAction>
<prism:PopupChildWindowAction.ContentTemplate>
<DataTemplate>
<StackPanel Width="Auto" ScrollViewer.VerticalScrollBarVisibility="Hidden" Height="Auto"
MaxHeight="400" >
<ScrollViewer BorderThickness="1" VerticalScrollBarVisibility="Auto" Width="820" Height="Auto" MaxHeight="200">
<StackPanel Height="Auto" >
<controls:XRadGridView x:Name="UnitsGrid" ItemsSource="{Binding Units, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
MaxHeight="300"
IsReadOnly="True"
RowIndicatorVisibility="Collapsed"
CanUserInsertRows="False"
CanUserDeleteRows="False"
ShowColumnHeaders="False"
ShowGroupPanel="False"
CanUserResizeColumns="False"
CanUserReorderColumns="False"
AutoGenerateColumns="False">
<controls:XRadGridView.Columns>
<telerik:GridViewDataColumn Header="DUID"
DataMemberBinding="{Binding DisplayDuid}"
IsReadOnly="True"
Width="Auto" />
<telerik:GridViewDataColumn Header="ReasonTime">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<toolkit:TimePicker x:Name="ReasonTime"
VerticalAlignment="Center" HorizontalAlignment="Center" Width="20"
MaxWidth="20"
Loaded="ReasonTime_Loaded"
Format="HHmm"
Value="{Binding OfferHeader.ReasonTime, Mode=TwoWay}"
controls:TimePickerExtensions.UpdateBindingOnValueChanged="{Binding ApplyReasonToAll}"
TabIndex="0"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
</controls:XRadGridView.Columns>
</controls:XRadGridView>
ViewModel code
Inside constructor I have below code
_reasonTimeChangedCommand = new DelegateCommand(OnReasonTimeChanged);
command details
private readonly DelegateCommand _reasonTimeChangedCommand;
public ICommand ReasonTimeChanged => _reasonTimeChangedCommand;
public void OnReasonTimeChanged()
{
}
The InvokeCommandAction should be inside the popup, to be used within the popup... but you bind to the value to a property (ReasonTime), and that property should be responsible for notifying the change. That's the view model's job, not just being a container for commands or forwarding properties from some DTO.

My autogenerated columns do not show in Datagrid

Most posts regarding DataGrid AutoGenerateColumns seem to deal with how to circumvent default behavior. Unfortunately the default is what I am struggling with.
I have several database tables created with Entity Framework 6.0, and would like to display them in my View using one and the same Datagrid for all tables, and AutoGenerating the Columns. Desired outcome: for the class-specific Viewmodel bound at run-time, display the columns with headers and at least one row.
XAML of the View's user control:
<UserControl.Resources>
<DataTemplate x:Key="TableDataTemplate">
<DataGrid
AutoGenerateColumns="True"
GridLinesVisibility="All"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible">
</DataGrid>
</DataTemplate>
</UserControl.Resources>
<StackPanel Height="720" Width="980">
<!-- shows user which menu item was chosen -->
<TextBlock Text="{Binding DisplayName}"/>
<!-- defines the Input data grid for adding to DB table -->
<Grid Height="80" MaxHeight="200">
<ItemsControl
ItemsSource="{Binding CurrentDataTable}"
ItemTemplate="{StaticResource TableDataTemplate}">
</ItemsControl>
</Grid>
The user control is bound to a class-specific 'Class'TableViewModel, implemented for all Database Tables. One example:
class ClientsAdminTableViewModel : TableViewModel
{
// the Property the view binds to
private IList<Client> currentDataTable;
public ObservableCollection<Client> CurrentDataTable
{
get { return CollectionExtensions.ToObservableCollection<Client>(currentDataTable); }
set { currentDataTable = value; OnPropertyChanged("CurrentDataTable")}
}
public ClientsAdminTableViewModel()
{
DisplayName = Strings.ClientAdminDisplayName;
currentDataTable = context.Clients.ToList<Client>();
}
When bound to {Binding CurrentDataTable} this code produces a UI, where I can see a grey body (probably the rows) surrounded by horizontal and vertical scrollbars, on top of which one long line (which must be the header line), but no columns.
Any help is appreciated.
your datagrid needs to have its itemsource set
try adding
ItemsSource="{Binding CurrentDataTable}"
to the datagrid declaration. Also, do you get any binding errors in the output window?
This code now works (Thank you J. King!) The trick was to get rid of the data template, create the data grid right in the grid with the exact same binding statements.
<StackPanel Height="720" Width="980">
<!-- shows user which menu item was chosen -->
<TextBlock Text="{Binding DisplayName}"/>
<!-- defines the Input data grid for adding to DB table -->
<Grid Height="200" MaxHeight="400">
<DataGrid
ItemsSource="{Binding CurrentDataTableNew}"
AutoGenerateColumns="True"
GridLinesVisibility="All"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible" >
</DataGrid>
</Grid>
</userControl>

dynamically creating ids of macro components using arguments

I am trying to create two grids which perform exactly the same function without having to duplicate the code of the code for the grid twice. So, I decided to use a macro component. But, I am not sure how to create the ids of the components in the macro component dynamically. The code does the following:
The first grid(west region) has two rows with two textboxes. If I add "hello" to the first textbox in this grid then the value of the second textbox is also set to "hello".
The second grid(center region) has two rows with two textboxes. If I add "world" to the first textbox in this grid then the value of the second textbox is also set to "world"
The values of both textboxes in the first grid are now same i.e. "hello"
The values of both textboxes in the second grid are now same i.e. "world"
I created a zul file in which I use a macro component like so:
<?component name="mygrid1" macro-uri="grid1.zul" inline="true"?>
<zk>
<vbox hflex="1">
<borderlayout height="500px" width="500px">
<west size="50%">
<mygrid1 id="grid1" index="1" />
</west>
<center>
<mygrid1 id="grid2" index="2" />
</center>
</borderlayout>
</vbox>
</zk>
<zscript>
fillInDuplicateBox(String value, Textbox duplicateBox) {
if (!"".contentEquals(duplicateBox.value))
return;
duplicateBox.value = value;
}
</zscript>
</window>
Macro component is shown below:
<zk>
<vbox hflex="1">
<grid width="300px">
<rows>
<row> Box 1: <textbox id="${concat("newBox", arg.index)}" onChange="fillInDuplicateBox(${concat("newBox, arg.index)}.value, ${concat("duplicateBox", arg.index)})" hflex="1" /></row>
<row> Box 2: <textbox id="${concat("duplicateBox", arg.index)}" hflex="1" /></row>
</rows>
</grid>
</vbox>
</zk>
I also tried the following code to create the macro component
<zk>
<vbox hflex="1">
<grid width="300px">
<rows>
<row> Box 1: <textbox id="newBox${arg.index}" onChange="fillInDuplicateBox(newBox${arg.index}.value, duplicateBox${arg.index})" hflex="1" /></row>
<row> Box 2: <textbox id="duplicateBox${arg.index}" hflex="1" /></row>
</rows>
</grid>
</vbox>
</zk>
None of this works. I am not sure how to dynamically create the ids of the components in the macro component. The textbox ids of the first grid must be "newBox1", "duplicateBox1" and the textbox ids of the second grid must be "newBox2", "duplicateBox2"
Please point out if there is a better way of achieving this task.
Thanks,
Sony
I am not sure if it is possible to dynamically create ids like that, but I am pretty certain it would be better to avoid it. There are better ways to do this by avoiding the id problem altogether and to use data binding or event handling instead. Your example indicates the main purpose for each grid is to copy the value of one Textbox to a different Textbox - I imagine there is something bigger you are trying to achieve so let us know if this answer leads you to what you need or not.
I have simplified and expanded your sample to give two examples, the first uses the onChanging to immediately copy as you type. The second pair of boxes uses databinding.
<?component name="mygrid1" macro-uri="/grid1.zul" ?>
<zk>
<window>
<vbox hflex="1">
<mygrid1 id="grid1" myGridTitle="First" />
<mygrid1 id="grid2" myGridTitle="Another" />
</vbox>
</window>
</zk>
Here is the macro component in grid1.zul:
<zk>
<zscript><![CDATA[
String myBoundString = "initial value";
]]></zscript>
<vbox hflex="1">
<grid>
<rows>
<row>
<hbox><label value="${arg.myGridTitle}" /> Source</hbox>
<textbox id="originalText" hflex="1" onChanging="duplicateText.value = event.value" />
</row>
<row>
<hbox><label value="${arg.myGridTitle}" /> Source copies here:</hbox>
<textbox id="duplicateText" hflex="1" />
</row>
<row>
Bound to myBoundString:
<textbox id="boundText1" value="#{myBoundString}" hflex="1" />
</row>
<row>
Bound to boundText1:
<textbox id="boundText2" value="#{boundText1.value, load-when=boundText1.onChange}" hflex="1" />
</row>
</rows>
</grid>
</vbox>
</zk>
In the databinding example, you have to change "intial value" and then tab away before the binder updates boundText2. Also notice that the TextBoxes do have ids (boundText1 and boundText2) but that has no impact on achieving the expected functionality in multiple instances of the macro component.

Datagrid selecteditem is set back to null after EventToCommand is called

I have a Silverlight 4 app created in MVVMLight.
In a view I have a DataGrid that is bound to my ViewModel, which has SeletedItem bound to SelectedItem again in my ViwModel :
<sdk:DataGrid Name="MyGrid" AutoGenerateColumns="False" Grid.Row="3" MaxHeight="200" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" RowHeight="35"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, ValidatesOnNotifyDataErrors=False}" ItemsSource="{Binding Items}" >
This all works just as it should as in when I click on an item in the grid the SelectedItem in my ViewModel is set correctly.
Now I have added a button to the rows in the Datagrid and added an EventToCommand to the button which is bound to the same ViewModel:
<Button Content="Update" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<Command:EventToCommand Command="{Binding Source={StaticResource Locator}, Path=MainDialog.ButtonCommand, Mode=TwoWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
This command fires and works correctly The Problem Is the SelectedItem property that was set earlier, that I now want to use is set to null!!
Why is this EventToCommand resetting the SelecteedItem property and how do I stop it so I can use it???
you should shar your code to let us see what the problem is. But maybe problem is that you set selected item as object that isn't in your itemsource collection of datagrid. Try to set selecteditem like this
SelectedItem = Items.Where(x => x.Id == someId).First();
And see if this is the problem.. Ofcourse change condition in Where.. :)

Collapse/Visible UserControls on ButtonClick with MVVM - no swap mechanism -

In my scenario I have a MainView + MainViewModel, UserControl1 + UserControl 2.
In the MainView I have 2 buttons labeled: Button_ShowUserControl1 + Button_ShowUserControl2.
At the lower part of the MainView I have a "ContentGrid" which takes/should_take... every UserControl.
My goal:
When Button_ShowUserControl1 is clicked UserControl1 is Visible and UserControl2 OR any other UserControl must be set to Collapsed. Same is valid for Button_ShowUserControl2.
My problem:
1.) As the UserControls shall be loaded at application start how can I put them all together in the one "ContentGrid"? Thats actually not possible... so how can I make one UserControl visible while the other is in the same place/"ContentGrid" just collapsed ?
2.) As 1.) seems not possible how can I instantiate all UserControls at start of the application and make them only Visible/Collapsed when respective Button is clicked?
3.) As a UserControl has a property Visibility = Visible/Hidden/Collapsed, how can I bind to a property in a ViewModel return such a value like Collapsed? I only could get a boolean value like Visibility = false/true ?
My testcode:
<Grid x:Name="LayoutRoot" Background="#FFBDF5BD" ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition Height="96*" />
<RowDefinition Height="289*" />
</Grid.RowDefinitions>
<Grid HorizontalAlignment="Stretch" Name="MenuGrid" VerticalAlignment="Stretch" Background="#FFCECEFF">
<StackPanel Name="stackPanel1" Background="#FFEDFF00" Orientation="Horizontal">
<Button Content="User Data 1" Height="35" Name="button1" Command="{Binding Path=ShowUserControl1Command}" Width="150" Margin="100,0,0,0" />
<Button Content="User Data 2" Height="35" Name="button2" Width="150" Margin="100,0,0,0" />
</StackPanel>
</Grid>
<Grid Grid.Row="1" HorizontalAlignment="Stretch" Name="ContentGrid" VerticalAlignment="Stretch" Background="#FFB15454" />
</Grid>
<UserControl x:Class="SwapUserControls.MVVM.UserControl2"
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:vm="clr-namespace:SwapUserControls.MVVM.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Visibility="{Binding IsUserControl1Collapsed, Path=Value}">
<UserControl.Resources>
<vm:MainViewModel x:Key="MainViewModelID" />
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource MainViewModelID}" />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="228*" />
<RowDefinition Height="72*" />
</Grid.RowDefinitions>
<Button Content="UserControl2" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="112,27,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
<DataGrid HorizontalAlignment="Stretch" Name="dataGrid1" VerticalAlignment="Stretch" Background="#FFC046F8" />
</Grid>
public class MainViewModel : ViewModelBase
{
RelayCommand _ShowUserControl1Command;
private bool _IsUserControl1Collapsed;
public RelayCommand ShowUserControl1Command
{
get
{
if (_ShowUserControl1Command == null)
{
_ShowUserControl1Command = new RelayCommand( () => ShowUserControl1() );
}
return _ShowUserControl1Command;
}
}
public void ShowUserControl1()
{
_IsUserControl1Collapsed = true;
}
public bool IsUserControl1Collapsed
{
get
{
return _IsUserControl1Collapsed;
}
}
}
Yes the code is wrong, therefore I ask here :)
You only have 2 things wrong with this code.
1) You can't set the visibility of a usercontrol directly... you have to set it on a container:
<Grid Visibility="Collapsed">
<myControls:MyUserControl />
</Grid>
2) Visibility is not a boolean value, it is an enum. As such, you will need to use a converter to convert from boolean to Visibility. Observe:
<Window ...>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>
<Grid Visibility="{Binding ShouldShowUsercontrol1, Converter={StaticResource BoolToVis}}">
<myControls:MyUserControl />
</Grid>
</Window>
That should be it. Hope this helps.
There are other things that you are leaving clues about that might affect the ability of this to work. For example, you don't show the biggest container element... are you wrapping everything in a StackPanel? If you are wrapping everything in a Grid, for example, the controls will overlay everything in layers.
Try these changes I suggest... it should get you closer.
Edit: Another idea using data templates
Another thing you could do is make sure you have a unique ViewModel for each of these views you want to show and hide:
public class MyFirstViewModel : ViewModel
{
}
public class MySecondViewModel : ViewModel
{
}
Then from your "parent" or "main" ViewModel, you show or hide the views you want by virtue of having them in a collection of ViewModels:
public MyMainViewModel : ViewModel
{
public ObservableCollection<ViewModel> ViewsToShow
{
...
}
public void ShowFirstViewModel()
{
ViewsToShow.Add(new MyFirstViewModel());
}
}
To wire everything up in your view, you would then datatemplate these types with their user controls (but this would not cause those views to be instantiated unless they were needed:
<Window ...>
<Window.Resources>
<DataTemplate DataType="{x:Type myViewModels:MyFirstViewModel}">
<myViews:MyFirstView />
</DataTemplate>
<DataTemplate DataType="{x:Type myViewModels:MySecondViewModel}">
<myViews:MySecondView />
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding ViewsToShow}" />
</Window>
And for any ViewModels you put in "ViewsToShow", the view will automatically see that and template in the appropriate view. Again, without instantiating it before it's needed.
This is probably a little cleaner than putting everything single thing in the View and setting visibility, but it would be dependent on you have a unique view model type for every view, which might not be the case.
The question of saving state comes up when using the DataTemplated approach. The solution here is to tread your ViewModel as the state of the control and design both your ViewModels and your Views accordingly. Here is an example that allows you to swap out your Views using DataTemplating, but switching back and forth saves state.
Assume you have the setup from the last section w/ 2 viewmodels that have datatemplates defined. Let's change up the MainViewModel a little:
public MyMainViewModel : ViewModel
{
public RelayCommand SwapViewsCommand
{
...
}
public ViewModel View
{
...
}
private ViewModel _hiddenView;
public MyMainViewModel()
{
View = new MyFirstViewModel();
_hiddenView = new MySecondViewModel();
SwapViewsCommand = new RelayCommand(SwapViewModels);
}
public void SwapViewModels()
{
var hidden = _hiddenView;
_hiddenView = View;
View = hidden;
}
}
And a few changes to the main view. I've omitted the DataTemplates for brevity.
<Window ...>
<!-- DataTemplates Here -->
<Button Command="{Binding SwapViewsCommand}">Swap!</Button>
<ContentControl Content="{Binding View}" />
</Window>
That's it. The secret here is I'm saving the reference to the original view model. This way, let's say there is a string property in a viewmodel and an associated textbox in the DataTemplated usercontrol with a two-way binding then the state will essentially be saved.