WPF Xaml processing order - wpf

I have a simple address form for a customer. The country and state combo boxes are linked to ListCollectionViews. This is so when the user changes the country settings, the state list can be filtered in the model view. The problem is that when the form loads some previous information, the state combo box is blank even through it has data. It seems to be because of the order in they are placed in the xaml. If I put the country combo box before the state, if works fine, but I would like the country to come after the state. Is there a way I can leave the xaml layout the way it is, but have the country combo box processed before the state?
Xaml:
<StackPanel Orientation="Horizontal">
<TextBlock Height="23" Name="tbkMailState" Text="State/Province:" Width="80" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2" Foreground="Black" />
<ComboBox Height="23" Name="cmbMailState" Width="200" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2" Foreground="Black" ItemsSource="{Binding GeoStateListMail}" SelectedValue="{Binding OpenEntityListing.EntityMailAddress.GeoState_Id}" DisplayMemberPath="Name" SelectedValuePath="Id" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Height="23" Name="tbkMailCountry" Text="Country:" Width="80" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2" Foreground="Black" />
<ComboBox Height="23" Name="cmbMailCountry" Width="200" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2" Foreground="Black" ItemsSource="{Binding GeoCountryListMail, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValue="{Binding OpenEntityListing.EntityMailAddress.GeoCountry_Id}" DisplayMemberPath="Name" SelectedValuePath="Id" IsSynchronizedWithCurrentItem="True" />
</StackPanel>
ViewModel filter:
public void GeoCountry_CurrentChanged(object sender, EventArgs e)
{
GeoStateList.Filter = item =>
{
GeoState vitem = item as GeoState;
if ((OpenEntityListing == null) || (vitem == null))
{
return false;
}
return vitem.GeoCountry_Id == OpenEntityListing.EntityAddress.GeoCountry_Id;
};
}

It would be bad to be dependent on the order of the xaml being processed.
Try to find a fitting event in your ViewModel when the ComboBox should be updated and bind the View to perhaps an extra property on the ViewModel.

Related

Add textbox value to datagrid row in wpf

I'm developing a WPF application with MVVM pattern.
I'm populating data to gridview and on selection, I populate to textbox,combobox to edit the contents. then Save the data.
Now I want to add new data to the grid view.
User input to textbox,combo box should be add to grid view and the i need to save it to data base.
my XMAL is as follows:
<Grid Height="444" Width="486">
<Label Content="Script" Height="28" HorizontalAlignment="Left" Margin="13,36,0,0" Name="lblScript" VerticalAlignment="Top" />
<TextBox Height="142" HorizontalAlignment="Left" Margin="75,38,0,0" Name="txtScript" VerticalAlignment="Top" Width="380" Text="{Binding Script_Text}"/>
<Button Content="Add" Command="{Binding SaveData}" Height="23" HorizontalAlignment="Left" Margin="284,409,0,0" Name="btnSave" VerticalAlignment="Top" Width="75" />
<Button Content="Reset" Command="{Binding ClearData}" Height="23" HorizontalAlignment="Left" Margin="380,409,0,0" Name="btnReset" VerticalAlignment="Top" Width="75" />
<DataGrid ItemsSource="{Binding Path=Param}" HeadersVisibility="Column" SelectionMode="Single"
AlternatingRowBackground="Gainsboro" Background="White" AutoGenerateColumns="False"
ItemContainerStyle="{StaticResource itemstyle}" CanUserAddRows="True" GridLinesVisibility="None"
Height="150" HorizontalAlignment="Left" HorizontalContentAlignment="Left" IsEnabled="True"
IsReadOnly="True" Margin="75,200,0,0" Name="dgMain" RowHeight="23" VerticalAlignment="Center"
VerticalContentAlignment="Center" Width="380" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding EditData}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Name}" Header="Parameter" Width="140" />
<DataGridTextColumn Binding="{Binding Path=Type}" Header="Type" Width="100" />
<DataGridTextColumn Binding="{Binding Path=Value}" Header="Value" Width="120" />
</DataGrid.Columns>
</DataGrid>
<Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="75,186,0,0" Name="lblName" VerticalAlignment="Top" />
<TextBox Height="23" Text="{Binding SelectedItem.Name, ElementName=dgMain}" HorizontalAlignment="Left" Margin="75,213,0,0" Name="txtName" VerticalAlignment="Top" Width="110" />
<Label Content="Type" Height="28" HorizontalAlignment="Left" Margin="191,186,0,0" Name="lblType" VerticalAlignment="Top" />
<ComboBox Height="23" Text="{Binding SelectedItem.Type, ElementName=dgMain}" HorizontalAlignment="Left" Margin="191,213,0,0" Name="cboType" VerticalAlignment="Top" Width="110" ItemsSource="{Binding}">
<ComboBoxItem Content="integer" />
<ComboBoxItem Content="double" />
<ComboBoxItem Content="string" />
</ComboBox>
<Label Content="Value" Height="28" HorizontalAlignment="Left" Margin="307,186,0,0" Name="lblValue" VerticalAlignment="Top" />
<TextBox Height="23" Text="{Binding SelectedItem.Value, ElementName=dgMain}" HorizontalAlignment="Left" Margin="307,213,0,0" Name="textBox1" VerticalAlignment="Top" Width="110" />
<TextBlock HorizontalAlignment="Left" Margin="428,15,0,0" Height="32">
<ToggleButton x:Name="Add" Focusable="False" Command ="{Binding AddNew}" Style="{StaticResource SMToggle}" >
<Image Source="/Image/Add.png" Width="16" Height="16" />
</ToggleButton>
</TextBlock>
</Grid>
Please help me.
SN
Assuming that you are correctly using MVVM, that your Param property is a collection and that your SelectedItem property is of the same type as the items in the collection, you should just be able to do this in the SaveData command in your view model:
Param.Add(SelectedItem);
If you are using MVVM, then instead of binding TextBox/ComboBox directly to the datagrid, bind them to a property of type Parameter (e.g. public Parameter DisplayedParameter {get; set; }) in your ViewModel. Then support two modes for your TextBox/ComboBox - Add and Edit.
When in Add Mode, simply set the DisplayedParameter to a new instance of Parameter. On click of Save button, save the parameter to the backend and refresh the value of Param Property (which I assume is the List/ObservableCollection of Parameter). Alternatively, add the parameter to the Param list, and then save the parameter to the backend.
When in Edit Mode, simply set the DisplayedParameter to the value of SelectedItem (you will need a viewmodel property for SelectedItem also). On click of Save, you can save the edited parameter in backend and refresh the Param.
Notice that, by default, you remain in Add mode; and switch to edit mode only when user requests so (e.g. selecting an item in datagrid, or maybe clicking an edit button in the row of datagrid). Once you are done with the edit (e.g. user saves or cancels), you go back to your default mode.

Set Text on combobox while getting data

I'm trying to display a default text in a ComboBox while I'm fetching data from a source, but it doesn't show anything.
<ComboBox
Grid.Row="1"
Grid.Column="2"
Text="Hepper"
ItemsSource="{Binding Builds}"
SelectedItem="{Binding SelectedBuild}"
DisplayMemberPath="VersionNo"
IsReadOnly="True"
IsEnabled="{Binding SelectedBuildEnable}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Width="180"
Height="30"
MinWidth="180" />
you can try to set the ComboBox.SelectedValue Property instead of ComboBox.Text.
I prefer to show another TextBlock above the ComboBox to display a default text:
<!-- don't forget to define the converter in your resources -->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</UserControl.Resources>
<!-- your Control -->
<ComboBox
Grid.Row="1"
Grid.Column="2"
x:Name="ComboBoxElement"
ItemsSource="{Binding Builds}"
SelectedItem="{Binding SelectedBuild}"
DisplayMemberPath="VersionNo"
IsReadOnly="True"
IsEnabled="{Binding SelectedBuildEnable}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Width="180"
Height="30"
MinWidth="180" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
Visibility="{Binding IsEnabled, ElementName=ComboBoxElement, Converter={StaticResource BooleanToVisibilityConverter}}"
IsHitTestVisible="False"
Text="Hepper"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="15,5" />
I guessed that your ComboBox becomes enabled if the data is fetched. Otherwise you have to use another binding for the visibility.
According to MSDN, the ComboBox.Text property
Gets or sets the text of the currently selected item.
Therefore, you could temporarily add an item into your ComboBox with your required message, select it and then remove it before filling the ComboBox when your data arrives.

Why my autocompletebox does not display any data?

I have a silverlight autocompletebox, am adding the ItemsSource in the codebehind.cs , also set the valuemember path as well, it does not display any data.
here is the code,
<sdk:AutoCompleteBox Margin="105,2,40,0" ItemsSource="{Binding}" ValueMemberPath="Code" FilterMode="Contains" IsTextCompletionEnabled="True" x:Name="txtcode" Height="23" VerticalAlignment="Top" TabIndex="1" TabNavigation="Local" >
<sdk:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<TextBlock />
</DataTemplate>
</sdk:AutoCompleteBox.ItemTemplate>
</sdk:AutoCompleteBox>
txtcode.ItemsSource = collection;
Possibly you have missed this one inside the DataTemplate,
<TextBlock Text="{Binding Code}" />
You Should bind that textbox first
Sample Code:
<sdk:AutoCompleteBox Margin="105,2,40,0" ItemsSource="{Binding}" ValueMemberPath="Code" FilterMode="Contains" IsTextCompletionEnabled="True" x:Name="txtcode" Height="23" VerticalAlignment="Top" TabIndex="1" TabNavigation="Local" >
<sdk:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Item}" />
</DataTemplate>
</sdk:AutoCompleteBox.ItemTemplate>
</sdk:AutoCompleteBox>
txtcode.ItemsSource = collection;

Binding inside listbox itemtemplate problems

I have two separate binding problems with listboxes with an itemtemplate which contains a texbox.
1) One listbox binds to a list of strings. How can I display each string inside the textboxes created and allow two way binding at the same time? Two way binding isn't allowed without specifying a Path or XPath.
<ListBox Height="231" HorizontalAlignment="Left" Margin="0,167,0,0" Name="listBoxKeys" VerticalAlignment="Top" Width="219" ItemsSource="{Binding Path=SelectedPlatform.Keys}" SelectedItem="{Binding Path=SelectedKey,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox Text="{Binding Mode=OneWay}" Margin="0,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And 2) I use another listbox which binds to some generic list of a custom KeyValuePair class. The itemtemplate contains a textbox and combobox. The textbox text is bound to the key property of each KeyValuePair object and the combobox selecteditem to the value property. My problem is that I want the combo to get filled by a list of strings declared in my viewmodel which will be changing on runtime. The window's datacontext is the viewmodel where the list is declared. I don't know the exact syntax I need to use to bind the combobox itemssource there. Here's my code :
<ListBox Height="393" HorizontalAlignment="Left" Margin="0,72,0,0" Name="listBoxActions" VerticalAlignment="Top" Width="254" ItemsSource="{Binding Path=SelectedPlayer.ControlProfile.MappedActions}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox Text="{Binding Key, Mode=TwoWay,UpdateSourceTrigger=LostFocus}" Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<ComboBox Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center" ItemsSource="{Binding ?}" SelectedItem="{Binding Value, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem is that the two-way binding on the source itself cannot work because it would mean that the whole object (string), for which the data template is created, must be replaced when user changes the text in the text box. Obviously, this will not work. Two-way binding will work only on a writable property of the bound object.
In your case I would suggest creating a view model for the items in the list box (basically a view model for your strings) and expose a Value property on it and bind to it in the data template:
<ListBox Height="231" HorizontalAlignment="Left" Margin="0,167,0,0"
Name="listBoxKeys" VerticalAlignment="Top" Width="219"
ItemsSource="{Binding Path=SelectedPlatform.KeyViewModels}"
SelectedItem="{Binding Path=SelectedKey,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox Text="{Binding Value, Mode=TwoWay}" Margin="0,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
1) Pavlo Glazkov seems to have a good answer to me
2) This is down to the DataContext for the ComboBox now being the key value pair rather than the ViewModel. There may be other ways to do this but the one that I've used before is to set the bindings RelativeSource source back to it's parent ItemsControl.
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}
Something like:
<ListBox Height="393" HorizontalAlignment="Left" Margin="0,72,0,0" Name="listBoxActions" VerticalAlignment="Top" Width="254" ItemsSource="{Binding Path=SelectedPlayer.ControlProfile.MappedActions}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox Text="{Binding Key, Mode=TwoWay,UpdateSourceTrigger=LostFocus}" Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<ComboBox Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center" ItemsSource="{Binding DataContext.Keys, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" SelectedItem="{Binding Value, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

geting information from Treeview with HierarchicalDataTemplate

Good day!
I have such a template:
<common:HierarchicalDataTemplate x:Key="my2ndPlusHierarchicalTemplate" ItemsSource="{Binding Children}">
<StackPanel Margin="0,2,5,2" Orientation="Vertical" Grid.Column="2">
<CheckBox
IsTabStop="False"
IsChecked="False"
Click="ItemCheckbox_Click"
Grid.Column="1"
/>
<TextBlock Text="{Binding Name}" FontSize="16" Foreground="#FF100101" HorizontalAlignment="Left" FontFamily="Verdana" FontWeight="Bold" />
<TextBlock Text="{Binding Description}" FontFamily="Verdana" FontSize="10" HorizontalAlignment="Left" Foreground="#FFA09A9A" FontStyle="Italic" />
<TextBox Width="100" Grid.Column="4" Height="24" LostFocus="TextBox_LostFocus" Name="tbNumber"></TextBox>
</StackPanel>
</common:HierarchicalDataTemplate>
for a Treeview
<controls:TreeView x:Name="tvServices" ItemTemplate="{StaticResource myHierarchicalTemplate}" ItemContainerStyle="{StaticResource expandedTreeViewItemStyle}" Grid.Column="1" Grid.Row="2" Grid.ColumnSpan="3" BorderBrush="#FFC1BCBC" FontFamily="Verdana" FontSize="14">
</controls:TreeView>
I want to know the Name property of each TextBox in Treeview to make validation of each textbox such as:
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
tbNumber.ClearValidationError();
if ((!tbNumber.Text.IsZakazNumberValid()) && (tbNumber.Text != ""))
{
tbNumber.SetValidation(MyStrings.NumberError);
tbNumber.RaiseValidationError();
isValid = false;
}
else
{
isValid = true;
}
}
and I wnat to see what check boxes were checked
how can I do it?
I'm not sure I'm clear why you need to know the name.
In this case the sender parameter of the LostFocus event is the TextBox in question. Hence you could use:-
TextBox tb = (TextBox)sender;
Now use this tb variable instead of using tbNumber (which doesn't actually exist because its defined in a template).

Resources