I have the following xaml:
<UserControl ... xmlns:dd="clr-namespace:AttachedBehaviours.DragDrop;assembly=AttachedBehaviours" ... >
<UserControl.Resources>
<dd:BindingProxy x:Key="library_proxy"
Binding="{Binding SelectedItems.Count, ElementName=library}" />
</UserControl.Resources>
<ListView x:Name="library" ItemsSource="{Binding View}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Value" DisplayMemberBinding="{Binding Value}" />
</GridView.Columns>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="dd:DragDropMulti.DragDropPreviewControl">
<Setter.Value>
<dd:DragDropPreviewBase>
<dd:DragDropPreviewBase.Template>
<ControlTemplate TargetType="dd:DragDropPreviewBase">
<TextBlock x:Name="tblk__preview"
Text="{Binding Binding, Source={StaticResource library_proxy}}" />
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Binding, Source={StaticResource library_proxy}}"
Value="1">
<DataTrigger.Setters>
<Setter TargetName="tblk__preview"
Property="Text"
Value="{Binding Name}" />
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</dd:DragDropPreviewBase.Template>
</dd:DragDropPreviewBase>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</UserControl>
The problem I'm having is that if I generate my ICollectionView (the "View" in ItemsSource="{Binding View}") with a basic CollectionViewSource, everything works as expected. E.g.:
View = new CollectionViewSource { Source = Models }.View;
However, if I use something more complex, the Setter binding of the DataTrigger doesn't work. E.g.:
View =
new CollectionViewSource
{
Source =
Models
.Join(
Lookups
, model => model.ID
, lookup => lookup.Key
, (model, lookup) =>
new ComplexModel
{
ID = model.ID,
Name = model.Name,
Value = lookup.Value
}
)
}
.View;
I get the error:
System.Windows.Data Error: 40 : BindingExpression path error: 'Name' property not found on 'object' ''MainWindowViewModel' (HashCode=17911681)'.
MainWindowViewModel is the overall DataContext (outside of the UserControl), so it's as though the DataContext has not been set.
Why would this be the case?
Related
I have this style for ContentControl:
<UserControl.Resources>
<DataTemplate x:Key="textbox">
<TextBox Text="edit me"/>
</DataTemplate>
<DataTemplate x:Key="textblock">
<TextBlock Text="can't edit"/>
</DataTemplate>
<Style x:Key="ContentControlStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Content" Value="{Binding}"/>
<Setter Property="ContentTemplate" Value="{StaticResource textblock}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem},AncestorLevel=1}}"
Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource textbox}" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
And that codes:
<ListView.View>
<GridView x:Name="UGridview1">
<GridViewColumn Width=" 90">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl >
</ContentControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
But I want to create the columns dynamically, so I wrote the following codes:
for (x = 0; x <= Lvobj.obj.Length - 1; x++) // ClmnCount - 1
{
GridViewColumn_ = new GridViewColumn();
GridViewColumn_.SetValue(NameProperty, "Column" + x);
GridViewColumn_.Header = Lvobj.obj(x)(clmntxt);
GridViewColumn_.Width = 99;
/// This part doesnt work
ContentControl cntr = new ContentControl();
cntr.Style = this.Resources("ContentControlStyle");
///
GridViewColumn_.CellTemplate = cntr.ContentTemplate;
UGridview1.Columns.Add(GridViewColumn_);
}
It never works. What must i do for i can create columns with ContentControl Style?
Either use XamlReader.Parse API with a DynamicResource:
const string Xaml = #"<DataTemplate " +
#"xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""> " +
#"<ContentControl Style=""{DynamicResource ContentControlStyle}"" />" +
"</DataTemplate>";
DataTemplate DataTemplate_ = System.Windows.Markup.XamlReader.Parse(Xaml) as DataTemplate;
GridViewColumn_.CellTemplate = DataTemplate_;
Or create a FrameworkElementFactory:
FrameworkElementFactory cc = new FrameworkElementFactory(typeof(ContentControl));
cc.SetValue(ContentControl.StyleProperty, this.Resources["ContentControlStyle"]);
GridViewColumn_.CellTemplate = new DataTemplate() { VisualTree = cc };
Access the ResourceDictionary with square brackets (it's a Dictionary):
this.Resources["ContentControlStyle"]
Make sure to not lookup the style before UserControl.OnInitialized is called. You can override OnInitialized in your UserControl and then initialize the ListView. Alternatively handle the Loaded event.
Alternatively (recommended), consider to define the style implicit (without a x:key). Then the style will be applied automatically, once the target is loaded. This way you don't have to deal with the resources.
You can limit the scope by defining it inside the ResourceDictionary of the ListView:
<ListView>
<ListView.Resources>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Content"
Value="{Binding}" />
<Setter Property="ContentTemplate"
Value="{StaticResource textblock}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem},AncestorLevel=1}}"
Value="True">
<Setter Property="ContentTemplate"
Value="{StaticResource textbox}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.View>
<GridView x:Name="UGridview1">
<GridViewColumn Width=" 90">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I have this XAML in a WPF project.
<ListView Grid.Column="0" Grid.Row="3" Grid.RowSpan="4" Grid.ColumnSpan="2" ItemsSource="{Binding FilteredApps}" SelectedItem="{Binding SelectedApp, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Status">
<GridViewColumn.CellTemplate >
<DataTemplate DataType="{x:Type Image}">
<Image>
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding State, Mode=TwoWay}" Value="{x:Static common:Globals+ModelState.Unedited}">
<Setter Property="Source" Value="../Resources/IndraDoc/AppUnedited.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding State, Mode=TwoWay}" Value="{x:Static common:Globals+ModelState.Edited}">
<Setter Property="Source" Value="../Resources/IndraDoc/AppEdited.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="{x:Static common:Globals+ModelState.New}">
<Setter Property="Source" Value="../Resources/IndraDoc/AppNew.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="{x:Static common:Globals+ModelState.Deleted}">
<Setter Property="Source" Value="../Resources/IndraDoc/AppDeleted.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Namn" Width="Auto"/>
</GridView>
</ListView.View>
</ListView>
When property "Name" changes the appropriate change is displayed in the Grid, but when the property "State" changes the Image is not switched. Any takes on this?
EDIT
Strange thing is this, I edited the XAML and added the image outside of the ListView bound it to the SelectedApp instead, this works fine, however that was not the intended functionality. It will work for now, but I am still curious as to why my original code won't trigger.
Adding the code where I do the statechange:
private void ChangeStateOnSelected(Globals.ModelState newState)
{
if (SelectedApp.State != newState) //dont do any changework if new state is same as current sate
{
if (SelectedApp.State == Globals.ModelState.New && newState == Globals.ModelState.Edited) //this is not allowed
return;
SelectedApp.State = newState;
if (newState == Globals.ModelState.Unedited)
{
var selectedId = SelectedApp.Id;
_allApps.Replace(_allApps.FirstOrDefault(x => x.Id == SelectedApp.Id), SelectedApp);
_selectedApp = (Data.DataModel.App)_locationService.GetApp(selectedId);
_filteredApps.Replace(_filteredApps.FirstOrDefault(x => x.Id == SelectedApp.Id), SelectedApp);
}
NotifyOfPropertyChange(() => SelectedApp);
NotifyOfPropertyChange(() => AllApps);
NotifyOfPropertyChange(() => FilteredApps);
}
}
I have in my XAML a Datatemplate like this:
<DataTemplate x:Key="SheetToTemplate">
<TextBox Name="_txtToSheet"
Text="{Binding Path=SHEET_TO, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource DigitOnlyTextBoxStyle}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.FilterTextChangedCommand }" >
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</DataTemplate>
This is my viewmodel with essential part:
RelayCommand _filterTextChangedCommand;
public ICommand FilterTextChangedCommand
{
get
{
if (_filterTextChangedCommand == null)
{
_filterTextChangedCommand = new RelayCommand(
param => TextChange(param),
param => true);
}
return _filterTextChangedCommand;
}
}
private object TextChange(object param)
{
throw new NotImplementedException();
}
This is the error I get in output:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Controls.UserControl',
AncestorLevel='1''.
BindingExpression:Path=DataContext.FilterTextChangedCommand;
DataItem=null; target element is 'InvokeCommandAction'
(HashCode=46858895); target property is 'Command' (type 'ICommand')
I don't understand why the event isn't fired.
Any suggestion?
ps. Note that the property of the textbox is correctly bound.
EDIT
here the full control
<ListView Grid.Row="0"
ItemsSource="{Binding Path=SelectedOperations}"
Margin="5,10,5,5"
Name="WorkOrders"
SelectionMode="Single"
FontSize="13"
Background="AliceBlue"
BorderBrush="AliceBlue">
<!--Style of items-->
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<!--Properties-->
<Setter Property="Control.HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Control.VerticalContentAlignment" Value="Center" />
<!--Trigger-->
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView >
<GridViewColumn Header="Operation" CellTemplate="{StaticResource DetailIdenTemplate}" Width="300"/>
<GridViewColumn Header="From" CellTemplate="{StaticResource SheetFromTemplate}" Width="50"/>
<GridViewColumn Header="To" CellTemplate="{StaticResource SheetToTemplate}" Width="50" />
</GridView>
</ListView.View>
</ListView>
And here the ViewModel class definition:
public class OperativeSheetSelectionViewModel : ViewModelBase
{
//
}
I did it.
The error was on the AncestorType. I need a Window, not an UserControl. (...)
Refering to my last question https://stackoverflow.com/questions/18568850/binding-in-combobox-xaml-wpf, I add a property FirstName in ClientBookKeepingViewModel. But now I got an error .
When I debug using breakpoint, the error comes from View.ShowDialog()
My code is as follows:
public int InsertClientBooking(int clientID, ClientBookKeepingViewModel k)
{
var client = new ClientBooking();
using (var context = new ProActiveDBEntities())
{
client.ClientBookID = k.ClientBookKeepingID;
client.ClientID = k.ClientID;
client.EmployeeID = k.EmployeeID;
client.WorkType = k.WorkType;
client.DateRecorded = (DateTime)k.DateRecorded;
client.BookingFormCompleted = k.BookingFormCompleted;
client.TimeBudgetCompleted = k.TimeBudgetCompleted;
client.ProposedCompletionDate = (DateTime)k.ProposedCompletionDate;
client.IsCompleted = k.IsCompleted;
client.FirstName = k.FirstName;
context.Clients.First(i => i.ClientID == clientID).ClientBookings.Add(client);
context.SaveChanges();
return clientID;
}
}
private void btnAddBooking_Click(object sender, RoutedEventArgs e)
{
ClientBookingView view = new ClientBookingView();
ClientBookKeepingViewModel book = new ClientBookKeepingViewModel();
book.Client = (ClientViewModel)this.DataContext;
book.Mode = Mode.Add;
view.DataContext = book;
view.ShowDialog();
}
ClientBookingView.Xmal
<ComboBox Grid.Column="1" Grid.Row="1" Name="cbEmployeeName"
ItemsSource="{Binding Source={StaticResource Employee}}"
DisplayMemberPath="FirstName"
SelectedValue="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True}"
SelectedValuePath="FirstName"
<CheckBox Grid.Column="1" Grid.Row="5" Name="dpBookingCompleted" Content="Yes"
IsChecked="{Binding BookingFormCompleted, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True}"/>
/>
<StackPanel Grid.Row="9" Orientation="Horizontal" Grid.ColumnSpan="3" Margin="0,5,20,0" Grid.Column="1">
<Button x:Name="btnUpdate" Width="80" Margin="10" Height="25"
Command="{Binding ShowUpdateCommand}"
Click="btnUpdate_Click">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MetroButton}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Mode}">
<DataTrigger.Value>
<enum:Mode>Add</enum:Mode>
</DataTrigger.Value>
<Setter Property="Content" Value="Add"/>
</DataTrigger>
<DataTrigger Binding="{Binding Mode}">
<DataTrigger.Value>
<enum:Mode>Edit</enum:Mode>
</DataTrigger.Value>
<Setter Property="Content" Value="Save"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=cbEmployeeName, Path=(Validation.HasError)}" Value="false"/>
<Condition Binding="{Binding ElementName=cbWorkType, Path=(Validation.HasError)}" Value="false"/>
<Condition Binding="{Binding ElementName=dpDateRecord, Path=(Validation.HasError)}" Value="false"/>
<Condition Binding="{Binding ElementName=dpProposedCompletionDate, Path=(Validation.HasError)}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button x:Name="btnCancel" Content="Cancel" Margin="10" Width="80" Height="25"
Command="{Binding CancelCommand}"
Click="btnCancel_Click" />
</StackPanel>
ClientBookListView
<ListView Name="lsvClientOwnerTypeList" Height="150" Width="700"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
HorizontalAlignment="Center"
VerticalAlignment="Top"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding ClientBookList}"
SelectedItem="{Binding SelectedClientBook}">
<ListView.View>
<GridView>
<GridViewColumn CellTemplate="{StaticResource RowButtons}"/>
<GridViewColumn Header="Completed" DisplayMemberBinding="{Binding IsCompleted}" />
<GridViewColumn Header="Employee Name" DisplayMemberBinding="{Binding FirstName}" />
<GridViewColumn Header="Work Type" DisplayMemberBinding="{Binding WorkType}" />
<GridViewColumn Header="Date Recorded" DisplayMemberBinding="{Binding DateRecorded, StringFormat={}\{0:dd/MM/yyyy\}}" />
<GridViewColumn Header="Booking Form Completed" DisplayMemberBinding="{Binding BookingFormCompleted}" />
<GridViewColumn Header="Time Budget Completed" DisplayMemberBinding="{Binding TimeBudgetCompleted}" />
<GridViewColumn Header="Proposed Completion Date" DisplayMemberBinding="{Binding ProposedCompletionDate, StringFormat={}\{0:dd/MM/yyyy\}}" />
</GridView>
</ListView.View>
</ListView>
I'm answering in an answer so that the comments don't get too big.
Your inner Exception says:
"The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_ClientBooking_Employee\". The conflict occurred in database \"ProActiveDB\", table \"dbo.Employee\", column 'EmployeeID'.\r\nThe statement has been terminated."
This means that you have a Foreign Key in your Employee table on the EmployeeID column called FK_ClientBooking_Employee and that there was a conflict when you tried to insert something into the database... if the conflict is with this column, then that normally means that you were trying to insert a row into another table that references this column in the Employee table, but your EmployeeID value was not in the Employee table.
UPDATE >>>
I'm not sure that you really needed to add that column into the database... that's what view models are for. You have the FirstName values in your Employee class (in C#), so all you needed to do was to add a new property into your view model class and fill it from your Employee class... then, you would be able to correctly bind to it.
Remember, the view model should hold all of the data that our view requires... but this doesn't mean that our view models have to match some database tables. In fact, there is often very little correlation between view models and database tables... it is the data type, or model, classes that match the database tables.
<ListView ItemsSource="{Binding MyData}">
<ListView.View>
<GridView>
<GridViewColumn Header="col1" DisplayMemberBinding="{Binding Path=value1}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Right" Text="{Binding Path=value1}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="col2">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Center" Text="{Binding Path=value2}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="col3" DisplayMemberBinding="{Binding Path=value3}"/>
</GridView>
</ListView.View>
<ListView ItemsSource="{Binding MyData}">
col1 is supposed to be right-aligned. (Not working)
col2 is supposed to be center-aligned. (Working)
col3 is supposed to be left-aligned. (Working)
Is there a reason DisplayMemberBinding is overriding CellTemplate? If so, is there a fix for this (while still using DisplayMemberBinding)?
Edit: I ended up implementing it like this:
<Window xmlns:util="clr-namespace:TestProject.Util">
<Window.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
<Style TargetType="GridViewColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
</Style>
<DataTemplate x:Key="value1Template">
<TextBlock TextAlignment="Right" Text="{Binding Path=value1}"/>
</DataTemplate>
<DataTemplate x:Key="value2Template">
<TextBlock TextAlignment="Right" Text="{Binding Path=value2}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListView ItemsSource="{Binding MyData}" IsSynchronizedWithCurrentItem="True" util:GridViewSort.Command="{Binding SortCommand}">
<ListView.View>
<GridView>
<GridViewColumn Header="col1" CellTemplate="{StaticResource value1Template}" util:GridViewSort.PropertyName="value1"/>
<GridViewColumn Header="col2" CellTemplate="{StaticResource value2Template}" util:GridViewSort.PropertyName="value2"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
In the code behind:
private RelayCommand sortCommand;
public ICommand SortCommand { get { return sortCommand ?? (sortCommand = new RelayCommand(Sort)); } }
private void Sort(object param)
{
var propertyName = param as string;
var view = CollectionViewSource.GetDefaultView(MyData);
var direction = ListSortDirection.Ascending;
if (view.SortDescriptions.Count > 0)
{
var currentSort = view.SortDescriptions[0];
if (currentSort.PropertyName == propertyName)
direction = currentSort.Direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
view.SortDescriptions.Clear();
}
if (!string.IsNullOrEmpty(propertyName))
view.SortDescriptions.Add(new SortDescription(propertyName, direction));
}
DisplayMemberBinding has the highest priority. You can not use it combined with CellTemplate. See here in the remarks section.
If you want to right-or center-align the content, you must declare the CellTemplate with the binding (as you did) and remove the DisplayMemberBinding-attribute. If you also want to change the column header alignment, you must also set the GridViewColumn.Header-property.
Just add the following after your Window tag:
<Window.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Right" />
</Style>
</Window.Resources>