OnPropertyChange(null) makes hyperlink disappear - c#

Code first (upper TextBox is simplified for purpose of this question) :
<TextBlock
Style="{StaticResource FieldNameStyle }"
TextWrapping ="Wrap" Height="33" FontSize="12"
Visibility="Visible"
TextAlignment="Center"
Foreground="#FFFFFF"
Opacity="0.5"
Text="{Binding UnderLineMsg}">
<Hyperlink Name="PrivacyNoticeLink2"
Command="{Binding OpenPrivacyNoticeCommand}">
<TextBlock
Visibility="Visible"
Name="privacyNoticeText2"
Text="{Binding PrivacyNoticeButtonLabel,FallbackValue='privacy notice' ,UpdateSourceTrigger=PropertyChanged}"/>
</Hyperlink>
</TextBlock>
this is what it looks after the window loads for the first time : Under line msg filler: link
one of the events in the window triggers a call to
OnPropertyChanged(null);
the method triggers a "refresh" in all the members in the window that are subscribed to it with :
UpdateSourceTrigger=PropertyChanged
once called the Hyperlink element disappears completely (verified using Snoop 2.8)
so after the call it will look like this:
Under line msg filler:
i have NO idea why this is happening. the current fix is replacing the general OnPropertyChanged call with many specific ones but that is not a realistic option in the long run.
EDIT :
Isolated the issue to a new project, note the issue still happens when its only a textblock within a textblock
simple XAML with a button that triggers OnPropertyChanged
<Grid>
<Button Click="Meh" Margin="171,37,153,199">
PRESS ME
</Button>
<TextBlock Name="WrapperText" Text= "{Binding randomNumber}">
<TextBlock Name="linkText" Text="{Binding randomNumStr }"></TextBlock>
</TextBlock>
</Grid>
Code behind:
public MainWindow()
{
DataContext = new Stuff();
InitializeComponent();
}
public void Meh(object sender, RoutedEventArgs e)
{
//MessageBox.Show(this, "BLA", "caption", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
//MessageBox.Show("FASDFASDFASDF");
(DataContext as Stuff).OnPropertyChanged(null);
//Msg.ShowMessageBox("BLA", "caption", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
}
the "view model"
public class Stuff : INotifyPropertyChanged
{
public Stuff()
{
rnd = new Random();
}
private Random rnd;
public int randomNumber => rnd.Next(1, 100);
public string randomNumStr => randomNumber.ToString()+"Text";
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Note the truly disgusting way that I trigger the property change. I know I should use Icommand in the 'Stuff' class but I wanted to isolate the problem quickly. In my original code, it's done properly.

Don't bind the Text property of a TextBlock that you are also adding a Hyperlink to. Raising the PropertyChanged event for the source property will then clear out the Hyperlink.
Instead of binding the Text property of the TextBlock itself, you could add a Run element to it:
<TextBlock
TextWrapping ="Wrap" Height="33" FontSize="12"
Visibility="Visible"
TextAlignment="Center"
Foreground="#FFFFFF"
Opacity="0.5">
<Run Text="{Binding UnderLineMsg, Mode=OneWay}" />
<Hyperlink Name="PrivacyNoticeLink2" Command="{Binding OpenPrivacyNoticeCommand}">
<TextBlock
Visibility="Visible"
Name="privacyNoticeText2"
Text="{Binding PrivacyNoticeButtonLabel,FallbackValue='privacy notice' ,UpdateSourceTrigger=PropertyChanged}"/>
</Hyperlink>
</TextBlock>

Related

C# UWP Button binding with flyout not refreshing button content

I have a button that displays the value from a class that I created. Everything works fine, except for the fact that the button content does not refresh once the value of the binding is changed in the code. If I exit the screen and come back, the value is correct. Staying on the same screen does not refresh the button content.
The button code is shown below.
<Grid x:Name="Task1Grid" Grid.Row="0" Grid.Column="0" Margin="5,0,5,0">
<Grid.RowDefinitions>
<RowDefinition Height=".2*"/>
<RowDefinition Height=".6*"/>
<RowDefinition Height=".2*"/>
</Grid.RowDefinitions>
<Button Grid.Row="1" Style="{StaticResource RoundedButtonStyle}" Tag="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="StoplightButton_Click" FontFamily="Global User Interface">
<Button.Content>
<Image Stretch="Uniform" Source="{Binding SelectedRepairOrder.TaskStatusGrid[0], Converter={StaticResource TaskStatusToStopLight}, Mode=OneWay}"/>
</Button.Content>
<Button.Background>
<ImageBrush Stretch="Uniform" ImageSource="{Binding SelectedRepairOrder.TaskStatusGrid[0], Converter={StaticResource TaskStatusToStopLight}, Mode=OneWay}"/>
</Button.Background>
</Button>
<Button x:Name="Task0Time" Tag="0" Style="{StaticResource RoundedButtonStyle}" Visibility="{Binding SelectedRepairOrder.TaskStatusGrid[0].NewTaskstatus, Converter=
{StaticResource TaskStatusToVisibility}}" IsEnabled="{Binding ShowForecastFeatures}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="{Binding SelectedRepairOrder.TaskStatusGrid[0].TmTimecmpltask, Converter={StaticResource TaskCompleteTimeToTime}}" Grid.Row="2" Flyout="{StaticResource Task1Flyout}"/>
<TextBlock Grid.Row="0" Text="{Binding ClientInfo.TasksInfo[0].TaskDescription}" TextAlignment="Center" VerticalAlignment="Bottom" FontSize="28"/>
</Grid>
The flyout code is shown below.
<Border x:Name="StopLightBorder" Background="CornflowerBlue" Grid.Row="1" BorderBrush="White" BorderThickness="2">
<Grid x:Name="StopLightGrid" Margin="5" >
<Grid.Resources>
<converter:TaskStatusToStopLight x:Key="TaskStatusToStopLight"/>
<converter:TaskCompleteTimeToTime x:Key="TaskCompleteTimeToTime"/>
<converter:TaskStatusToVisibility x:Key="TaskStatusToVisibility"/>
<Flyout x:Key="Task1Flyout" >
<ListBox ItemsSource="{Binding ForecastTimes}" Tag="0" SelectionChanged="ForecastTimeChanged"/>
</Flyout>
The code which changes the value for the binding is shown below.
private void ForecastTimeChanged(object sender, SelectionChangedEventArgs e)
{
var timeListBox = (ListBox)sender;
var completeTime = Convert.ToDateTime(e.AddedItems[0].ToString());
var taskNum = Convert.ToInt16(((FrameworkElement)sender).Tag);
var result = checkPreviousTaskTimes(completeTime, taskNum);
switch (result)
{
case ForecastResult.ValidTime:
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].TmTimecmpltask = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].DtDateoverride = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].TmTimeoverride = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].SendOverrideForecastTime = true;
globalContext.SelectedRepairOrder.WasChanged = true;
globalContext.SelectedRepairOrder.RecordGrid = "1";
((Popup)((FlyoutPresenter)((FrameworkElement)sender).Parent).Parent).IsOpen = false;
break;
default:
showForecastError(result, completeTime, taskNum);
break;
}
}
The Visibility and IsEnabled both work just fine. Not sure what else I can do at this point. It seems that changing the bound data does not have an effect until you leave the screen. I chased this issue all the way through and saw the changes to the data as well as everything else I expected. The flyout causes the forecasttimechanged method to activate. When we go to save this data to the database, the data is correct. The flyout shows the selected time when viewing it on the screen, which is what I want. I see that highlighted in the flyout.
If there is a better control to use than the button, I am all ears at this point. Here is the tricky part. This forecast time can be set in the application as well as the app you are seeing code from. The app has time in 15 minute increments, but the other program that can update this control can put in any time it wishes.
I know there is some control or parameter that needs to be set in order to make this happen properly, but for the life of me, I cannot find it. I have tried everything for the past 3 days now and nothing works.
Help me please.
I know there is some control or parameter that needs to be set in order to make this happen properly, but for the life of me, I cannot find it. I have tried everything for the past 3 days now and nothing works.
From your code, I guess the problem is that you have not implemented INotifyPropertyChanged for binding property. And your logic is complex, you could realize your feature with the easy way like the follow example.
<Button Content="{Binding SelectItem,Mode=OneWay}">
<Button.Flyout>
<Flyout Placement="Top">
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectItem,Mode=TwoWay}">
</ListBox>
</Flyout>
</Button.Flyout>
</Button>
Bind the button content with SelectItem, And then the button content will be modified automatically if the ListBox SelectedItem changed.
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public List<string> Items { get; set; } = new List<string>();
private string selectItem = "Nico";
public string SelectItem { get { return selectItem; } set { selectItem = value; OnPropertyChanged(); } }
public MainPageViewModel()
{
Items.Add("Nico");
Items.Add("Song");
Items.Add("Xiao");
}

Update TextBox content in WPF Window

I'm having a WPF Project, in which I'm performing an installation...
To show the installations progress, the window is containing a TextBox which I want to update using for example:
LogDisplay.AppendText("Initialising installation..." + "\r\n");
This works kind of...
The problem is that the content of the TextBox is only getting displayed when the installation is finished.
I have tried now several soluions, such as:
/*
LogDisplay.Update;
this.Update();
this.UpdateContent();
*/
But non of this was working for me...
The XAML code is:
<Window x:Class="Steam_Server_Installer.UI.ServerInstallation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Steam_Server_Installer.UI"
mc:Ignorable="d"
Title="" Height="600" Width="900"
WindowStartupLocation="CenterScreen">
<Grid>
<TextBox x:Name="LogDisplay" HorizontalAlignment="Left" VerticalAlignment="Top" Height="470" Width="650" Margin="30,90,0,0" IsReadOnly="True"/>
<Button x:Name="cancel_button" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,530,115,0" Width="70" Content="Cancel" FontSize="16" Click="cancel_button_Click"/>
<Button x:Name="finish_start_button" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,530,25,0" Width="70" Content="Finish" FontSize="16" Click="finish_start_button_Click" IsEnabled="False"/>
</Grid>
</Window>
If someone could tell me a working solution or point me to another question, which already discusses this question, it would be very much appreciated.
Try using TextBlock rather than TextBox like this
<TextBlock x:Name="LogDisplay" HorizontalAlignment="Left" VerticalAlignment="Top" Height="470" Width="650" Margin="30,90,0,0" />
It would be better to use binding instead of setting it like this. First you will implement INotifyPropertyChanged in your .xaml.cs file like
public class YourClassName: Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
//then create a string variable in your .xaml.cs file like
private string _logText;
public string LogText
{
get{ return _logText;}
set { _logText = value; OnPropertyChanged("LogText"); }
}
public YourClassName()
{
InitializeComponent();
//setting data context of the window
this.DataContext = this;
}
}
And in your XAML, use:
<TextBlock Text="{Binding LogText, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="470" Width="650" Margin="30,90,0,0" />
Now, you can just update LogText variable inside your class like
this.LogText = this.LogText + "Initialising installation..." + "\r\n"; //or better use StringBuilder and Append function
When use long task in UI thread, UI thread is not idle to update other content control.
You must use create other thread and handle task to thread then update the UI control by using the UI thread.
1.Create Thread or Task
2.Work task ...
3.Update the UI thread with Application.Current.Dispatcher.Invoke(() => { TextBox.Text = "text"; });
4.Finish

PropertyChangedCallback not firing when respective property bound to ScrollBar

I have a rather simple UserControl which I would like to extend with the DependencyProperty. The relevant code of the control is as follows:
public partial class CompassControl : UserControl
{
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(Double), typeof(CompassControl),
new FrameworkPropertyMetadata( 0.0, FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnAngleChanged)));
private static void OnAngleChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
((CompassControl)target).SetImageAngle((Double)e.NewValue);
}
public CompassControl( )
{
InitializeComponent( );
}
public Double Angle
{
get { return (Double)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}
This control is being used on a simple form; the relevant XAML as follows:
<DockPanel DockPanel.Dock="Bottom">
<DockPanel>
<TextBlock DockPanel.Dock="Left"
TextAlignment="Center" FontWeight="Bold" FontSize="12"
Padding="0,4,0,0" HorizontalAlignment="Left"
Height="22" Width="60" Margin="10,0,0,0"
Text="{Binding ElementName=scrollBarAngle, Path=Value}">
</TextBlock>
<ScrollBar DockPanel.Dock="Left" Name="scrollBarAngle" Orientation="Horizontal"
Height="22" Margin="10,0"
Maximum="360.0" Minimum="0.0" SmallChange="1.0" Value="0.0"
ValueChanged="scrollBarAngle_ValueChanged" />
</DockPanel>
</DockPanel>
<ctl:CompassControl DockPanel.Dock="Top" Name="compassControl"
Margin="5" Width="Auto" Height="Auto"
Angle="{Binding ElementName=scrollBarAngle, Path=Value}"
/>
</DockPanel>
The "Text" property of the TextBox and the "Angle" property of my control are bound to the "Value" property of the ScrollBar using the following binding:
"{Binding ElementName=scrollBarAngle, Path=Value}"
When I scroll the ScrollBar, the Text field is updated as expected, but the Angle does not change - the OnAngleChanged callback is not being called!
However if I directly change the Angle property in the ScrollBar's ValueChanged event everything works fine - the property got changed and the respective callback fired:
private void scrollBarAngle_ValueChanged( object sender, RoutedPropertyChangedEventArgs<double> e )
{
compassControl.Angle = e.NewValue;
}
Please help resolve this issue!
Thank you,
--Alex
My apologies - the problem was not in the code, but in the environment! I had several copies of VS2013 open, the project was open in two of them. Anyway, after reading Clemens comment indicating that my problem is not reproducible, I closed all instances of VS, then started the fresh instance, opened the project - and everything worked fine!
Thank you!

Reach a TextBlock from a specific ListViewItem from the ListView in Windows Phone 8.1 XAML programmatically

I am a new developer on Windows Phone 8.1, I am try to reach a specific ListView item from the ListView collection and be able to color it or color the TextBock inside of it, But I can't reach the item or reach any of items inside of ListView, Please take a look for my below code :
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
SQLiteRT db1 = new SQLiteRT();
var db_connection = await db1.Connection("MyDB.sqlite");
List<MyTBL> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;");
db_connection.Close();
LV_Options.ItemsSource = t_list;
}
// my List View called LV_Options
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView lv1 = sender as ListView;
if (lv1 == null)
return;
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
tb.FontSize = 17; // here I got debug error (it not worked !!!!!!!)
var item = LV_Options.Items.ElementAt(3); // this seems not work also !!!!
item.BackColor = Color.LightSteelBlue;
}
As you can see above, I tried to reach a specific item by LV_Options.Items.ElementAt(3) but it doesn't work! I also tried to reach the TextBlock from the selected List view item, but also not worked !
(Updated)
XAML code :
<!-- Title Panel -->
<StackPanel Grid.Row="0" Margin="19,0,0,0">
<TextBlock Name="TB_Rslt" Text="Here result of your answer" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/>
<TextBlock Text="page title" Margin="0,-6.5,0,26.5" Style="{ThemeResource HeaderTextBlockStyle}" CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
</StackPanel>
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,10,19,15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="TB_Question" Text="Choose Answer " Margin="0,0,25,0" HorizontalAlignment="Right" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" />
<TextBlock Name="TB_EnWord" Text="" Margin="90,0,15,0" HorizontalAlignment="Left" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" TextAlignment="Right" />
<StackPanel Grid.Row="1" Margin="5,22,0,0">
<ListView Name="LV_Options" SelectionChanged="LV_Options_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding AMean1}" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<Button Name="Btn_Answer" Content="Ansewr" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Bottom" Click="Btn_Answer_Click"/>
My application is a quiz application that offer 4 choices/options as answers for each question, and when user select a true answer, I want to highlight the true answer(true choice) by make its background to green, and if the user selected wrong answer/option I want to make the background of that answer (a specific List View item) with red.
Any help please ?
You're not going to be able to access an element inside a data template like that. Instead, leverage the binding to a view model to set the color and other view-related properties. First, create a wrapper view model for your data class:
public class MyTBLViewModel : INotifyPropertyChanged
{
public MyTBL Entity
{
get { return _entity; }
}
private readonly MyTBL _entity;
public Brush Highlight
{
get { return _brush; }
set
{
_brush = value;
RaisePropertyChanged("Highlight");
}
}
private Brush _highlight;
public double ItemFontSize
{
get { return _itemFontSize; }
set
{
_itemFontSize = value;
RaisePropertyChanged("ItemFontSize");
}
}
private Brush _itemFontSize;
public MyTBLViewModel(MyTBL entity)
{
_entity = entity;
_highlight = new SolidColorBrush(Colors.Transparent);
_itemFontSize = 12;
}
public event PropertyChangedEventArgs PropertyChanged;
protected void RaisePropertyChanged(string propName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propName));
}
}
Use this as your ItemsSource:
List<MyTBLViewModel> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;")
.AsEnumerable().Select(entity => new MyTBLViewModel(entity)).ToList();
Now in your view, bind the view elements to "Highlight" and "ItemFontSize", and to any other properties you like:
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6" Background="{Binding Highlight}">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding Entity.AMean1}" TextWrapping="Wrap"
FontSize="{Binding ItemFontSize}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Finally, you can get the data item from the SelectionChangedEventArgs -- use it to update your view-related properties:
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Color.LightSteelBlue);
item.ItemFontSize = 17;
}
foreach (var item in e.RemovedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Colors.Transparent);
item.ItemFontSize = 12;
}
}
var item = LV_Options.Items.ElementAt(3);
This line is incorrect. It will not return you a TextBlock. I don't know what a .BackColor is, and it should not compile. The Items property in a ListView will return you a list of ListViewItems. If you want to access the inside element from a ListViewItem, you'll need to access the ContentTemplateRoot property.
Do not use var ever. It lets you assume that you know the type, whereas if you explicitly typed the declaration you would realize you're doing it wrong.
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
What is a MyTBL type? FindName is only available to framework DependencyObjects so I'm assuming it's a user control? You have to provide a lot more code to show us what you're doing and what you're setting the ListView's ItemsSource and ItemTemplate with and what these errors are and how you have 2 breaking debug errors at once and what the error messages are.
Comprehending runtime error messages is a huge part of being a good developer.

How do I override or alter the inheritance of the child elements when my datacontext is set on the parent element?

Disclaimer I feel like this is a fairly simple question, so i must reiterate that I did look for an answer and couldn't find anything!
Not sure if I am asking the question correctly, but I will tell you this. I am working on becoming more familiar with MVVM, so I am messing around with a ridiculously simple project of two stackpanels, ten textboxes, and some simple binding. Everything works now, since I have two panels, which separates my boxes and lets me set two datacontext.
My question is this:
a) is it possible to set the datacontext on a parent element (Stackpanel) and have half of my child elements (textboxes) use that context via inheritance and then give the other half of the elements A DIFFERENT data context?
and
b) if this is possible, how??
Thanks people-smarter-than-I
Here is the code that is trying so hard, but not really doing anything I want it to be doing:
XAML
<Grid>
<StackPanel>
<StackPanel HorizontalAlignment="Left" Margin="8,8,0,75" Width="224" DataContext="{Binding Path=Customer}">
<TextBox Text="{Binding Path=FirstName}" Height="28" Name="label1"/>
<TextBox Text="{Binding Path=MiddleName}" Height="28" Name="l2"/>
<TextBox Text="{Binding Path=LastName}" Height="28" Name="l3"/>
<TextBox Text="{Binding Path=CompanyName}" Height="28" Name="l4"/>
<TextBox Text="{Binding Path=EmailAddress}" Height="28" Name="l5"/>
<!--I want the next five TextBox elements to bind to a different datacontext-->
<TextBox Text="{Binding Path=FirstName}" Height="28" Name="label11"/>
<TextBox Text="{Binding Path=MiddleName}" Height="28" Name="l21"/>
<TextBox Text="{Binding Path=LastName}" Height="28" Name="l1lgh3"/>
<TextBox Text="{Binding Path=CompanyName}" Height="28" Name="l1hj4"/>
<TextBox Text="{Binding Path=EmailAddress}" Height="28"/>
</StackPanel>
</StackPanel>
</Grid>
Code Behind C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new MainViewModel();
}
}
View Model
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
PopulateCustomerInfo();
}
private Customer customer;
public Customer Customer
{
get { return customer; }
set
{
customer = value;
OnPropertyChanged("Customer");
}
}
private Customer customer2;
public Customer Customer2
{
get { return customer2; }
set
{
customer2 = value;
OnPropertyChanged("Customer");
}
}
private void PopulateCustomerInfo()
{
AdventureWorksLTE ctx = new AdventureWorksLTE();
this.Customer = (from c in ctx.Customers
select c).FirstOrDefault();
this.Customer2 = (from c in ctx.Customers
orderby c.FirstName descending
select c).FirstOrDefault();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handle = PropertyChanged;
if (handle != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handle(this, e);
}
}
}
Well, you can do various things like changing the DataContext locally on all those lower TextBoxes:
<TextBox Text="{Binding Path=FirstName}" DataContext="..."/>
The question really is though: What do you want to achieve? Does it make sense to even set the DataContext on the StackPanel?
Maybe you should not and use a longer path instead:
<TextBox Text="{Binding Path=Customer.FirstName}" Height="28" Name="label1"/>
It all depends on what properties are going to be used in the child controls, if most or all of them are in Customer, sure, set it to keep the bindings short. If you need the main view model in some places you can use RelativeSource to get to a control with the right data context and change the path accordingly. (DataContext.*, data context appears in the path as a different source is specified)
most simple way is to have two StackPanels within the first one, grouping 5 by 5 your textboxes, first one having customer as datacontext, second one having customer2.
The stackPanel and (5 textboxes) content could even be defined as a DataTemplate (having no key or name but rather a DataType = {x:Type local:Customer } (where local is your clr namespace) ) and you would just use 2 ContentPresenters having content binding to Customer / Customer 2, that would 'automatically' display what you want.

Categories