UPDATE 3
" I want that the text from this TextBox should be shown in another TextBox(in another view)"
The second textbox in another view is meant to show other information that is tied to the first textbox, but not the copy.
So the user control contains a text box for, say, Bus code. Once I enter bus code, tabbing out will trigger a fetch from the database for other details such as bus name, bus destination, bus model etc.
The others textbox which is in another view then displays the bus name. All following textboxes display destination and so forth. When the command is invoked, and I try to write to the property BusName, it gets assigned (and I call Notify("BusName")) but it does not show on the UI.
Hope that was more clear. Sorry for any confusion caused :).
UPDATE2 - Response to blindmeis
Thanks for your reply though this not appear to be what I was looking for. The tab out is essential because that is how management wants their pages to be populated i.e. when you tab out of a 'code' textbox after entering the code, it will use the code to fetch data from the db to populate the rest of the controls. This does not appear to have the tab-out behavior in it. As for the 3rd dependency property, it is in my original code, I simply did not include it here because the value in the first textbox (user control tabout textbox) is not relevant to the problem. Simply, what I am trying to accomplish is that the second textbox must populate when you tab-out of the first textbox.
I could do this with an eventhandler, but wanted to use commands. I am thinking now perhaps commands are not the way to go here and I should switch to using an event handler.
Please advise if you still have any ideas on how to get the second textbox to populate when you tab out of the first (by putting a breakpoint in populate, you will see that the property gets assigned. ). If I have not understood correctly or missed something here, please let me know. Thanks!
UPDATE!
I have created a VS2013 solution mimicking my code, which reproduces the problem. It is at this public google drive link as a zip file (takes a few seconds for the download icon to appear):
https://drive.google.com/file/d/0B89vOvsI7Ubdbk85SVlvT3U2dVU/view?usp=sharing
You will see that the 2nd text box does not update despite the bound property storing the new value.
Greatly appreciate any help. Thanks in advance.
Original post:
I have a textbox control to which I have tied a key binding based command to go process some actions (in a method that the command has been tied to) when the user hits tab while in the textbox (tabs out).
I have other controls in that page that are boiund to properties in the viewmodel that I write to in that tab-out connected function. When I write my properties in the constructor or somewhere 'outside' that command invokation they seem to work fine and the values show on the page, but when I write them within that command invocation, the properties in the vm contain the values but don't show up on the UI
Any ideas why and how to fix?
Thanks much in advance
From XAML:
<TextBox Name="txtCode" Text="{Binding Path=CodeValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Key="Tab" Command="{Binding RetrieveRecordCmd}" > </KeyBinding>
</TextBox.InputBindings>
</TextBox>
From VM:
RetrieveRecordCmd = new GSSCommand(RetrieveRecord, param => this.CanExecuteRetrieveRecordCmd);
Command tied function:
public void RetrieveRecord(object obj)
{
objPie = null;
//Check if a record exists for that code
gssSvcMethodStatusBase = gssSvcClientBase.ReadPies(ref gssSvcGlobalVarsBase, out objPie, out grfaBase, CodeValue);
if ((objPie != null)) // && (objPie.DateCreated > DateTime.MinValue))
PopulatePage(objPie);
else if (objPie == null)
InitiateCreateNew();
else
return;
}
It looks like you have implemented the INotifyPropertyChanged interface in the strict sense, but are missing the actual functionality. The interface itself doesn't automatically give you change notifications. You also need to fire the PropertyChanged event when each property changes. The standard pattern looks like:
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
_name = value;
NotifyPropertyChanged("Name");
}
}
You should make a habit of writing all mutable properties which you intend to bind to the UI in this format. Snippets can make this easier to do consistently.
this works, but i dont know if this is the behavior you want.
<UserControl x:Class="ProblemDemoWPF.TextBoxTabOutUserControl"
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"
Name="TabOutTextUserControl"
>
<StackPanel Margin="0,0,0,0" Orientation="Horizontal">
<Label Content="UserControl (ucTextBox)->"></Label>
<TextBox Width="80" Height="30" BorderBrush="Black"
BorderThickness="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding ElementName=TabOutTextUserControl, Path=CodeValue}">
</TextBox>
</StackPanel>
</UserControl>
new Dependency Propertie with right binding without DataContext
public partial class TextBoxTabOutUserControl : UserControl
{
public static readonly DependencyProperty CodeValueProperty =
DependencyProperty.Register("CodeValue", typeof(string), typeof(TextBoxTabOutUserControl));
public string CodeValue
{
get { return (string)GetValue(CodeValueProperty); }
set { SetValue(CodeValueProperty, value); }
}
public TextBoxTabOutUserControl()
{
InitializeComponent();
}
}
Just bind both to LocTextBoxText
<Window x:Class="ProblemDemoWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:ProblemDemoWPF"
Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<loc:TextBoxTabOutUserControl Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="left"
CodeValue="{Binding Buscode, Mode=OneWayToSource}"/>
<Label Content="Busname" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left"></Label>
<TextBox Width="100" Grid.Row="1" Grid.Column="1" Text="{Binding Path=Busname, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Label Content="Busdestination" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Left"></Label>
<TextBox Width="100" Grid.Row="2" Grid.Column="1" Text="{Binding Path=Busdestination, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Label Content="Busmodel" Grid.Row="3" Grid.Column="0" HorizontalAlignment="Left"></Label>
<TextBox Width="100" Grid.Row="3" Grid.Column="1" Text="{Binding Path=Busmodel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</Grid>
</Window>
add Notify to property setter
class MainWindowViewModel : INotifyPropertyChanged
{
private String _buscode;
private string _busname;
private string _busdestination;
private string _busmodel;
public String Buscode
{
get
{
return _buscode;
}
set
{
if (_buscode != value)
{
_buscode = value;
Notify("Buscode");
FetchData(_buscode);
}
}
}
private void FetchData(string buscode)
{
//DB stuff
this.Busname = "Name 1234";
this.Busmodel = "Model 1234";
this.Busdestination = "Destination 1234";
}
public string Busname
{
get { return _busname; }
set { _busname = value; Notify("Busname"); }
}
public string Busdestination
{
get { return _busdestination; }
set { _busdestination = value; Notify("Busdestination"); }
}
public string Busmodel
{
get { return _busmodel; }
set { _busmodel = value; Notify("Busmodel"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Related
I am trying to get from my database one item, and when clicking on the next or previous button, I would like to get the next item out of my database by increasing its ID. I'm at the point of having my first item in my card, but when I click on previous or next, nothing happens.
I have in xaml:
<smtx:XamlDisplay Key="cards_1" Margin="4 4 0 0">
<materialDesign:Flipper Style="{StaticResource MaterialDesignCardFlipper}">
<materialDesign:Flipper.FrontContent>
<Grid Height="350" Width="200">
<Grid.RowDefinitions>
<RowDefinition Height="250" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<materialDesign:ColorZone Mode="PrimaryMid" VerticalAlignment="Stretch">
<materialDesign:PackIcon Kind="AccountCircle" Height="128" Width="128"
VerticalAlignment="Center" HorizontalAlignment="Center" />
</materialDesign:ColorZone>
<StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding CurrentGebruiker.Naam}"></TextBlock>
<Button Style="{StaticResource MaterialDesignFlatButton}" Foreground="DarkGoldenrod"
Command="{x:Static materialDesign:Flipper.FlipCommand}"
Margin="0 4 0 0"
>SHOW DETAILS</Button>
</StackPanel>
</Grid>
</materialDesign:Flipper.FrontContent>
And my viewmodel:
public ZoekMatchViewModel()
{
LeesGebruiker(1);
KoppelenCommands();
}
private Gebruiker currentGebruiker;
public Gebruiker CurrentGebruiker
{
get
{
return currentGebruiker;
}
set
{
currentGebruiker = value;
NotifyPropertyChanged();
}
}
private void KoppelenCommands()
{
NextCommand = new BaseCommand(VolgendeGebruiker);
PrevCommand = new BaseCommand(VorigeGebruiker);
}
public ICommand NextCommand { get; set; }
public ICommand PrevCommand { get; set; }
private void LeesGebruiker(int id)
{
//instantiƫren dataservice
ZoekMatchDataService zoekMatchDS =
new ZoekMatchDataService();
currentGebruiker = zoekMatchDS.GetGebruiker(id);
}
public void VolgendeGebruiker()
{
if (CurrentGebruiker != null)
{
int id = (currentGebruiker.ID) + 1;
LeesGebruiker(id);
}
}
public void VorigeGebruiker()
{
if (CurrentGebruiker != null)
{
int id = (currentGebruiker.ID) - 1;
LeesGebruiker(id);
}
}
My buttons:
<Button Command="{Binding PrevCommand}" Background="Transparent" BorderThickness="0" Height="50">
and
<Button Command="{Binding NextCommand}" Background="Transparent" BorderThickness="0" Height="50">
So the problem is that my xaml doesn't update to the new user when I click on next or previous buttons.
If you need more information, I'm happy to provide!
I think the "right" way to do this would be with a custom Selector. Selector is the base class used in WPF for a control that lets the user select one or more items from a list. For example: ComboBox and ListBox are both Selectors. If you've never made a custom control before this may be a bit complicated though.
There are a few simpler answers. You could make a class that holds all the data you want to display for a single option, and have your Window define a property of that type. Then you could bind all the variable parts of your interface to that property and just change the property value manually when the user clicks the left or right button. Of course you'd only be able to use this on one place, whereas if you made a custom control you could reuse it anywhere, any number of times.
I have a textbox and a datagrid like so:
<Page
TextElement.FontSize="14" FontFamily="Segoe UI"
Title="Delivery View">
<Page.Resources>
<xcdg:DataGridCollectionViewSource x:Key="firstNameDataSource"
Source="{Binding Path=Accessor.Views[FirstNameView].SourceCollection}"
AutoFilterMode="And"
DistinctValuesConstraint="Filtered">
<xcdg:DataGridCollectionViewSource.ItemProperties>
<xcdg:DataGridItemProperty Name="FirstName" CalculateDistinctValues="False"/>
</xcdg:DataGridCollectionViewSource.ItemProperties>
</xcdg:DataGridCollectionViewSource>
</Page.Resources>
<ScrollViewer Name="pendingScroll" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
<DockPanel Name="pnlMainPanel" LastChildFill="True" Style="{StaticResource panelBackground}">
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" FontSize="18" Text="Pending Guests" Margin="0,1,3,1" Foreground="SteelBlue" HorizontalAlignment="Left"/>
<TextBox Name="txtFirstNameFilter" Grid.Row="1" >
</TextBox>
<xcdg:DataGridControl x:Name="gridPendingGuests" Margin="5,0,5,1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MinHeight="100"
MinWidth="200"
CellEditorDisplayConditions="None"
EditTriggers="None"
ItemScrollingBehavior="Immediate"
AutoCreateColumns="False"
SelectionMode="Single"
NavigationBehavior="RowOnly"
ItemsSource="{Binding Source={StaticResource firstNameDataSource}}">
<xcdg:DataGridControl.View>
<xcdg:TableView ShowRowSelectorPane="False"/>
</xcdg:DataGridControl.View>
<xcdg:DataGridControl.Columns>
<xcdg:Column x:Name="FirstName" FieldName="FirstName" Title="First Name" Width="150" />
</xcdg:DataGridControl.Columns>
<i:Interaction.Behaviors>
<utils:UpdateDataGridOnTextboxChange/>
</i:Interaction.Behaviors>
</xcdg:DataGridControl>
</Grid>
</DockPanel>
</ScrollViewer>
</Page>
In the datagrid, you have a collection of first names. This works perfectly. The display is good. As you can see, I added an Interactions.Behavior class which currently handles a filter with a hard coded value when the user clicks on the datagrid with their mouse. The filtering works fine. If there is a first name of "John", that record is removed from view, leaving all other records in place.
Here is that code:
using System.Windows.Interactivity;
using System.Windows;
using Xceed.Wpf.DataGrid;
using System;
namespace Some.Namespace.Behaviors
{
public class UpdateDataGridOnTextboxChange : Behavior<DataGridControl>
{
protected override void OnAttached()
{
AssociatedObject.MouseUp += AssociatedObjectOnMouseUp;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.MouseUp -= AssociatedObjectOnMouseUp;
base.OnDetaching();
}
private void AssociatedObjectOnMouseUp(object sender, RoutedEventArgs routedEventArgs)
{
var items = AssociatedObject.Items;
items.Filter = CollectionFilter;
}
private bool CollectionFilter(object item)
{
System.Data.DataRow dr = item as System.Data.DataRow;
//set the ItemArray as Guest
Guest guest = SetGuest(dr);
if (guest.FirstName.Equals("John"))
{
return false;
}
return true;
}
private Guest SetGuest(System.Data.DataRow dr)
{
Guest guest = new Guest();
guest.FirstName = dr.ItemArray[0].ToString();
return guest;
}
public class Guest
{
public string FirstName { get; set; }
}
}
}
This works as expected. Again, when the user clicks on the datagrid, the filter filters out the users with the First Name of "John".
What I WANT to have happen is for the user to be able to type a first name in the txtFirstNameFilter Textbox and the datagrid to then filter the records that contain the text in the first name, keeping them visible and the others without that first name to not be visible.
The way I can do it is with an attached property of the Textbox TextChanged property? That's a question, because I don't know how to do an attached property and then how to make sure that when that attached property actually changes, call the AssociatedObjectOnMouseUp method to run the filtering.
System.Windows.Interactivity.Behavior<T> inherits from DependencyObject. So give it a dependency property and bind that.
public class UpdateDataGridOnTextboxChange : Behavior<DataGrid>
{
#region FilterValue Property
public String FilterValue
{
get { return (String)GetValue(FilterValueProperty); }
set { SetValue(FilterValueProperty, value); }
}
public static readonly DependencyProperty FilterValueProperty =
DependencyProperty.Register(nameof(FilterValue), typeof(String), typeof(UpdateDataGridOnTextboxChange),
new FrameworkPropertyMetadata(null, FilterValue_PropertyChanged));
protected static void FilterValue_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as UpdateDataGridOnTextboxChange).OnFilterValueChanged(e.OldValue);
}
private void OnFilterValueChanged(object oldValue)
{
// Do whatever you do to update the filter
// I did a trace just for testing.
System.Diagnostics.Trace.WriteLine($"Filter value changed from '{oldValue}' to '{FilterValue}'");
}
#endregion FilterValue Property
/*****************************************
All your code here
*****************************************/
}
XAML:
<i:Interaction.Behaviors>
<utils:UpdateDataGridOnTextboxChange
FilterValue="{Binding Text, ElementName=txtFirstNameFilter}"
/>
</i:Interaction.Behaviors>
You should rename it, though. It's got nothing to do with text boxes. You could bind FilterValue to a viewmodel property, or the selected value in a ComboBox, or whatever.
Update
OP's having trouble with the binding only updating FilterValue when the text box loses focus. This isn't what I'm seeing, but I don't know what's different between the two.
There isn't any UpdateTargetTrigger property of Binding, but you can swap the source and the target when both are dependency properties of dependency objects. This works for me:
<TextBox
x:Name="txtFirstNameFilter"
Text="{Binding FilterValue, ElementName=DataGridFilterThing, UpdateSourceTrigger=PropertyChanged}"
/>
<!-- snip snip snip -->
<i:Interaction.Behaviors>
<local:UpdateDataGridOnTextboxChange
x:Name="DataGridFilterThing"
/>
</i:Interaction.Behaviors>
More specifically, am I doing it right because it is not working. I have a bool property in my ViewModel along with a text property for a TextBlock. If I change the text property, the results appear on screen immediately. So I know something is listening for the property changes. The visibility property is set to use a bool-to-visibility converter but that converter never gets called. I'm sure it is just some part of the data binding that I am not doing right but I have tried everything suggested on StackOverflow as well as setting the binding manually and several other things. I have over 12 hours in this problem and am feeling really let down by the whole Silverlight / MVVM architecture in general. And I was so excited that I "figured it out", too.
Particulars: Silverlight 5.1.10144
App.xaml resources:
<Application.Resources>
<vidstreams:ManagementViewModel x:Key="managementViewModel"/>
<vidstreams:VisibilityConverter x:Key="visConverter"/>
</Application.Resources>
MyView.xaml DataContext:
<UserControl.DataContext>
<Binding Source="{StaticResource managementViewModel}"/>
</UserControl.DataContext>
MyView.xaml Grid visibility binding:
<Grid x:Name="LayoutRoot" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid ...
Visibility="{Binding IsWaitingVisible, Mode=OneWay, Converter={StaticResource visConverter}}">...</Grid>
<Button x:Name="test"
Click="test_Click"
Content="test visibility"
HorizontalAlignment="Left"
Margin="0,0,0,0"
Grid.Row="1"
VerticalAlignment="Top"/>
</Grid>
MyView.xaml.cs Instance property and test_Click code:
public ManagementViewModel DataContextObject
{
get
{
return (ManagementViewModel)App.Current.Resources["managementViewModel"];
}
}
protected void test_Click(object sender, RoutedEventArgs e)
{
DataContextObject.IsWaitingVisibile = !DataContextObject.IsWaitingVisibile; //doesn't toggle the visibility or cause the converter to be hit
DataContextObject.WaitingText = "Loading data..."; //works
}
ManagementViewModel class innards:
public class ManagementViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
bool mIsWaitingVisible = true;
public bool IsWaitingVisibile
{
get { return mIsWaitingVisible; }
set
{
mIsWaitingVisible = value;
OnPropertyChanged("IsWaitingVisible");
}
}
...
}
I would post the converter code here but it isn't even being hit. It's a simple converter like the others found in various posts on this site, anyway.
Any thoughts or suggestions - or just confirmation that this is some sort of regression bug in 5 maybe? - would be so appreciated. Perhaps the visibility binding instructions have to be set differently. Remember, the TextBlock works fine:
<TextBlock x:Name="WaitingTextBlock"
Text="{Binding WaitingText}" .../>
#GolfARama
Hi can you try with this
Visibility="{Binding IsWaitingVisible, Mode=TwoWay, Converter={StaticResource visConverter}}">
I am very new to Silverlight and WP7 and am writing my first app. I spent a great deal of time trying to figure out what aids to use and my choice came down to Caliburn Micro or MVVM toolkit and after seeing the video on MVVM toolkit, I chose it. But I am having a really difficult time getting it to work like was shown in the Laurent's MIX10 video. I could not find any full example of the code so I had to watch the video almost frame by frame to duplicate what Laurent did and I am only halpf way done. I have the basic code in place and it seems to be hitting my service but is not showing on my WP7 phone emulator. A side question, is the working example posted anywhere? I was hoping someone could look at my code and tell me where I am going wrong. Here it is. When I run the project, there are no errors, the emulator comes up fine but the text does not show that is being returned from the service. I have been developing .Net apps for a long time but am a noob to Silverlight and Asynchronous WCF services. Any help would be appreciated. BTW, the app is very simple, all it does is return a random bible verse from a WCF service I set up at http://www.rjmueller.com/DataAccessService/StoneFalcon.svc and displays it through a method called GetRandomBibleVerseById that takes no parameters and returns an entity called Bible. That's it, very simple. I know the answer is going to be very obvious but what I don't know, I don't know.
This is my ServiceHelper that communicates with my Service:
public class ServiceHelper
{
public void GetRandomBibleVerseById(Action<Bible, Exception> callback)
{
var client = new StoneFalconClient();
client.GetRandomBibleVerseByIdCompleted += (s, e) =>
{
var userCallback = e.UserState as Action<Bible, Exception>;
if (userCallback == null)
{
return;
}
if (e.Error != null)
{
userCallback(null, e.Error);
return;
}
};
client.GetRandomBibleVerseByIdAsync(callback);
}
Here is my MainViewModel:
public class MainViewModel : INotifyPropertyChanged
{
/// <summary>
/// The <see cref="BibleVerse" /> property's name.
/// </summary>
public const string BibleVersePropertyName = "BibleVerse";
private Bible _bibleVerse;
public Bible BibleVerse
{
get
{
return _bibleVerse;
}
set
{
if (_bibleVerse == value)
{
return;
}
_bibleVerse = value;
RaisePropertyChanged(BibleVersePropertyName);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string ApplicationTitle
{
get
{
return "RJ's Bible Searcher";
}
}
public string PageName
{
get
{
return "Verse of the Day";
}
}
public MainViewModel()
{
ServiceHelper helper = new ServiceHelper();
helper.GetRandomBibleVerseById((bibleVerse, error) =>
{
if (error != null)
{
//show error
}
else
{
BibleVerse = new Bible();
}
});
}
}
Here is my Xaml page: (the field I am binding to right now is called Text, yes, I know, not the best name, I am going to change that but for now that's what it is)
<phone:PhoneApplicationPage x:Class="BibleSearcher.wp7.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:BibleSearcher.wp7.ViewModel"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait"
Orientation="Portrait"
mc:Ignorable="d"
d:DesignWidth="480"
d:DesignHeight="768"
shell:SystemTray.IsVisible="True"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<UserControl.Resources>
<!--not the best way to do this,
does not allow the constructor to take paramaters, uses default constructor
when the xaml reaches this point, the viewmodel is created-->
<vm:MainViewModel x:Key="MainViewModel" />
</UserControl.Resources>
<!--LayoutRoot contains the root grid where all other page content is placed-->
<Grid x:Name="LayoutRoot"
Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel"
Grid.Row="0"
Margin="24,24,0,12">
<TextBlock x:Name="ApplicationTitle"
Text="RJ's Bible Searcher"
Style="{StaticResource PhoneTextNormalStyle}" />
<TextBlock x:Name="PageTitle"
Text="Verse of the Day"
Margin="-3,-8,0,0"
Style="{StaticResource PhoneTextTitle1Style}" FontSize="48" />
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentGrid"
Grid.Row="1"
DataContext="{Binding Source={StaticResource MainViewModel}}" >
<TextBlock Text="{Binding Path=Text}"
Style="{StaticResource PhoneTextNormalStyle}"
FontSize="28" Margin="17,8,18,8" d:LayoutOverrides="Width" TextWrapping="Wrap" VerticalAlignment="Top" />
</Grid>
</Grid>
Yes, you are binding to a property called "Text" as you point out, but I do not see such a property being exposed by your ViewModel!
Is this actually a property of the BibleVerse object? If so, your binding path should be "BibleVerse.Text"
I am creating a WPF window with a DataGrid, and I want to show the blank "new item" row at the bottom of the grid that allows me to add a new item to the grid. For some reason, the blank row is not shown on the grid on my window. Here is the markup I used to create the DataGrid:
<toolkit:DataGrid x:Name="ProjectTasksDataGrid"
DockPanel.Dock="Top"
Style="{DynamicResource {x:Static res:SharedResources.FsBlueGridKey}}"
AutoGenerateColumns="False"
ItemsSource="{Binding SelectedProject.Tasks}"
RowHeaderWidth="0"
MouseMove="OnStartDrag"
DragEnter="OnCheckDropTarget"
DragOver="OnCheckDropTarget"
DragLeave="OnCheckDropTarget"
Drop="OnDrop"
InitializingNewItem="ProjectTasksDataGrid_InitializingNewItem">
<toolkit:DataGrid.Columns>
<toolkit:DataGridCheckBoxColumn HeaderTemplate="{DynamicResource {x:Static res:SharedResources.CheckmarkHeaderKey}}" Width="25" Binding="{Binding Completed}" IsReadOnly="false"/>
<toolkit:DataGridTextColumn Header="Days" Width="75" Binding="{Binding NumDays}" IsReadOnly="false"/>
<toolkit:DataGridTextColumn Header="Due Date" Width="75" Binding="{Binding DueDate, Converter={StaticResource standardDateConverter}}" IsReadOnly="false"/>
<toolkit:DataGridTextColumn Header="Description" Width="*" Binding="{Binding Description}" IsReadOnly="false"/>
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
I can't figure out why the blank row isn't showing. I have tried the obvious stuff (IsReadOnly="false", CanUserAddRows="True"), with no luck. Any idea why the blank row is disabled? Thanks for your help.
You must also have to have a default constructor on the type in the collection.
Finally got back to this one. I am not going to change the accepted answer (green checkmark), but here is the cause of the problem:
My View Model wraps domain classes to provide infrastructure needed by WPF. I wrote a CodeProject article on the wrap method I use, which includes a collection class that has two type parameters:
VmCollection<VM, DM>
where DM is a wrapped domain class, and DM is the WPF class that wraps it.
It truns out that, for some weird reason, having the second type parameter in the collection class causes the WPF DataGrid to become uneditable. The fix is to eliminate the second type parameter.
Can't say why this works, only that it does. Hope it helps somebody else down the road.
Vincent Sibal posted an article describing what is required for adding new rows to a DataGrid. There are quite a few possibilities, and most of this depends on the type of collection you're using for SelectedProject.Tasks.
I would recommend making sure that "Tasks" is not a read only collection, and that it supports one of the required interfaces (mentioned in the previous link) to allow new items to be added correctly with DataGrid.
In my opinion this is a bug in the DataGrid. Mike Blandford's link helped me to finally realize what the problem is: The DataGrid does not recognize the type of the rows until it has a real object bound. The edit row does not appear b/c the data grid doesn't know the column types. You would think that binding a strongly typed collection would work, but it does not.
To expand upon Mike Blandford's answer, you must first assign the empty collection and then add and remove a row. For example,
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// data binding
dataGridUsers.ItemsSource = GetMembershipUsers();
EntRefUserDataSet.EntRefUserDataTable dt = (EntRefUserDataSet.EntRefUserDataTable)dataGridUsers.ItemsSource;
// hack to force edit row to appear for empty collections
if (dt.Rows.Count == 0)
{
dt.AddEntRefUserRow("", "", false, false);
dt.Rows[0].Delete();
}
}
Add an empty item to your ItemsSource and then remove it. You may have to set CanUserAddRows back to true after doing this. I read this solution here: (Posts by Jarrey and Rick Roen)
I had this problem when I set the ItemsSource to a DataTable's DefaultView and the view was empty. The columns were defined though so it should have been able to get them. Heh.
This happned to me , i forgot to new up the instance and it was nightmare for me . once i created an instance of the collection in onviewloaded it was solved.
`observablecollection<T> _newvariable = new observablecollection<T>();`
this solved my problem. hope it may help others
For me the best way to implement editable asynchronous DataGrid looks like that:
View Model:
public class UserTextMainViewModel : ViewModelBase
{
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
this._isBusy = value;
OnPropertyChanged();
}
}
private bool _isSearchActive;
private bool _isLoading;
private string _searchInput;
public string SearchInput
{
get { return _searchInput; }
set
{
_searchInput = value;
OnPropertyChanged();
_isSearchActive = !string.IsNullOrEmpty(value);
ApplySearch();
}
}
private ListCollectionView _translationsView;
public ListCollectionView TranslationsView
{
get
{
if (_translationsView == null)
{
OnRefreshRequired();
}
return _translationsView;
}
set
{
_translationsView = value;
OnPropertyChanged();
}
}
private void ApplySearch()
{
var view = TranslationsView;
if (view == null) return;
if (!_isSearchActive)
{
view.Filter = null;
}
else if (view.Filter == null)
{
view.Filter = FilterUserText;
}
else
{
view.Refresh();
}
}
private bool FilterUserText(object o)
{
if (!_isSearchActive) return true;
var item = (UserTextViewModel)o;
return item.Key.Contains(_searchInput, StringComparison.InvariantCultureIgnoreCase) ||
item.Value.Contains(_searchInput, StringComparison.InvariantCultureIgnoreCase);
}
private ICommand _clearSearchCommand;
public ICommand ClearSearchCommand
{
get
{
return _clearSearchCommand ??
(_clearSearchCommand =
new DelegateCommand((param) =>
{
this.SearchInput = string.Empty;
}, (p) => !string.IsNullOrEmpty(this.SearchInput)));
}
}
private async void OnRefreshRequired()
{
if (_isLoading) return;
_isLoading = true;
IsBusy = true;
try
{
var result = await LoadDefinitions();
TranslationsView = new ListCollectionView(result);
}
catch (Exception ex)
{
//ex.HandleError();//TODO: Needs to create properly error handling
}
_isLoading = false;
IsBusy = false;
}
private async Task<IList> LoadDefinitions()
{
var translatioViewModels = await Task.Run(() => TranslationRepository.Instance.AllTranslationsCache
.Select(model => new UserTextViewModel(model)).ToList());
return translatioViewModels;
}
}
XAML:
<UserControl x:Class="UCM.WFDesigner.Views.UserTextMainView"
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:model="clr-namespace:Cellebrite.Diagnostics.Model.Entities;assembly=Cellebrite.Diagnostics.Model"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:converters1="clr-namespace:UCM.Infra.Converters;assembly=UCM.Infra"
xmlns:core="clr-namespace:UCM.WFDesigner.Core"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<DockPanel>
<StackPanel Orientation="Horizontal"
DockPanel.Dock="Top"
HorizontalAlignment="Left">
<DockPanel>
<TextBlock Text="Search:"
DockPanel.Dock="Left"
VerticalAlignment="Center"
FontWeight="Bold"
Margin="0,0,5,0" />
<Button Style="{StaticResource StyleButtonDeleteCommon}"
Height="20"
Width="20"
DockPanel.Dock="Right"
ToolTip="Clear Filter"
Command="{Binding ClearSearchCommand}" />
<TextBox Text="{Binding SearchInput, UpdateSourceTrigger=PropertyChanged}"
Width="500"
VerticalContentAlignment="Center"
Margin="0,0,2,0"
FontSize="13" />
</DockPanel>
</StackPanel>
<Grid>
<DataGrid ItemsSource="{Binding Path=TranslationsView}"
AutoGenerateColumns="False"
SelectionMode="Single"
CanUserAddRows="True">
<DataGrid.Columns>
<!-- your columns definition is here-->
</DataGrid.Columns>
</DataGrid>
<!-- your "busy indicator", that shows to user a message instead of stuck data grid-->
<Border Visibility="{Binding IsBusy,Converter={converters1:BooleanToSomethingConverter TrueValue='Visible', FalseValue='Collapsed'}}"
Background="#50000000">
<TextBlock Foreground="White"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="Loading. . ."
FontSize="16" />
</Border>
</Grid>
</DockPanel>
This pattern allows to work with data grid in a quite simple way and code is very simple either.
Do not forget to create default constructor for class that represents your data source.