Control Enabled/Disabled behaviour in Silverlight - c#

I have an application that has a lot of textboxes in it. Also this textboxes actually are never disabled, but rather become ReadOnly instead. I prefer using one property of ContentControl for all my controls, but if i set IsEnabled to false all my textboxes become disabled. How can i make them go to readonly mode? I prefer not making my own control though, maybe i can use styles or something to reassign behaviour?
Edit: I actually am looking for solution that allows me to use binding to bind state of all controls(IsReadOnly) through binding in one place. Like:
<ContentControl IsEnabled="{Binding boolFlag, Mode=OneWay}">
<Grid x:Name="LayoutRoot" HorizontalAlignment="Stretch">
....
<TextBox/>
</Grid>
</ContentControl>

It seems like the use of the DataForm control would be best in your case. It would allow you to control every field within it as a group. It does provide the IsReadOnly option plus it comes with many free features that are very nice.
This video gives a good introduction
http://www.silverlight.net/learn/data-networking/data-controls/dataform-control
http://www.silverlightshow.net/items/Creating-Rich-Data-Forms-in-Silverlight-3-Introduction.aspx
http://www.silverlight.net/content/samples/sl4/toolkitcontrolsamples/run/default.html
Look for dataform
Cheers,

I suggest you to use a simple extension method for your ContentControl that will make textBoxes IsReadOnly = True. For example:
public static class ContentControlEx
{
public static void DisableTextBoxes(this ContentControl contentControl)
{
FrameworkElement p = contentControl as FrameworkElement;
var ts = p.GetChildren<TextBox>();
ts.ForEach(a => { if (!a.IsReadOnly) a.IsReadOnly = true; });
}
public static List<T> GetChildren<T>(this UIElement parent) where T : UIElement
{
List<T> list = new List<T>();
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++) {
UIElement child = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (child != null) {
if (child is T)
list.Add(child as T);
List<T> l1 = GetChildren<T>(child);
foreach (T u in l1)
list.Add(u);
}
}
return list;
}
}
usage (for ContentControl with Name = "content"):
content.DisableTextBoxes();
I have a XAML like this:
<Grid x:Name="LayoutRoot" Background="White">
<ContentControl IsEnabled="True" Name="content">
<StackPanel Margin="15">
<TextBox Width="150" Name="tb1" Margin="5" Text="{Binding tb1}" />
<TextBox Width="150" Name="tb2" Margin="5" Text="{Binding tb2}" />
<TextBox Width="150" Name="tb3" Margin="5" Text="{Binding tb3}"/>
<TextBox Width="150" Name="tb4" Margin="5" Text="{Binding tb4}"/>
<Button Name="bSubmit" Click="bSubmit_Click">Make Textboxes readonly</Button>
</StackPanel>
</ContentControl>
</Grid>
Let me know if it helps...

If I understand right, you want to bind each TextBox.IsReadOnlyProperty to a single bool.
You might try something like this, in a similar fashion to how you bound the IsEnabled property of the ContentControl:
<TextBox IsReadOnly="{Binding boolFlag, Mode=OneWay}" ... /> <!-- in each of your textboxes -->
This should provide you what you need: change boolFlag, every textbox turns on or off.

Related

Set Content of an Element without a Name-Property

I'm using a ListBox in combination with a ObservableCollection. The content is set via a TemplateSelector (TextBlock or Label). The text has to be formatted (f.e. with Run-Tags in Code-behind), but i can't access the Items. Is there a solution to get the elements?
I've tried the usage of OfType<>, but this works only on Panels. I searched for an children-attribute but, there isn't one for ListBoxes. Setting the Name-Property via binding is not possible for UId and Name.
An IEnumerator for the LogicalChildren doesn't work and iterate over the whole content everytime a new element is added, is not so optimal. Here a minimal example.
<Window.Resources>
<DataTemplate x:Key="TextBlockTemplate">
<StackPanel>
<TextBlock />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="LabelTemplate">
<StackPanel>
<Label/>
</StackPanel>
</DataTemplate>
<local:myTemplateSelector x:Key="myTemplateSelector" x:Name="myTemplateSelector" TextBlockTemplate="{StaticResource TextBlockTemplate}" LabelTemplate="{StaticResource LabelTemplate}"/>
</Window.Resources>
<Grid Margin="0">
<ListBox Name="mylist" Grid.Row="3"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding _listEntries}"
ItemTemplateSelector="{StaticResource myTemplateSelector}"
>
</ListBox>
</Grid>
Greetings and thanks :)
The TextBlock has an Inlines property that returns the Inline elements that comprise the contents of the TextBlock.
The Label has a Content property that you, depending on how you are using it, may cast to a Panel.
There are no inline elements for a TextBox.
Now, I found a solution. I made the TextBlock and Label as User Control and set the Name-property. In the code-behind, I have access to the DataContext and the element can set itself.
<UserControl x:Class="Test.TextBlockControl"
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:local="clr-namespace:TextBlockControl"
Loaded="UserControl_Loaded">
<Grid>
<StackPanel HorizontalAlignment="Stretch" Margin="0,0,0,0">
<TextBlock Name="textBlock"/>
</StackPanel>
</Grid>
In the Code behind i can now access the values and set:
public partial class TextBlockControl : UserControl
{
public List<string> name => DataContext as List<string>;
public TextBlockControl()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
foreach (var t in name)
{
var run = new Run(t.Text);
if (t.IsHighlighted)
{
run.Foreground = Brushes.Green;
}
else
{
run.Foreground = Brushes.Red;
}
textBlock.Inlines.Add(run);
}
}
}
}
In the MainWindow, the dataTemplate then references the UserControl (root is the namespace):
<root:PickControl />

Make specific ListView item not clickable in Universal Windows Platform

I am using a ListView element in my XAML:
<ListView
x:Name="myList"
IsItemClickEnabled="true"
ItemClick="onDrawerItemClick"
SelectionMode="Single"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.ItemTemplate>
<DataTemplate>
<Grid
Width="260">
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="44" />
<ColumnDefinition
Width="*" />
</Grid.ColumnDefinitions>
<Image
x:Name="image"
Source="{Binding myIcon}"
Grid.Column="0" />
<TextBlock
Text="{Binding myTxt}"
Grid.Column="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and I populate it using Bind property like so:
List<MyObj> listData = a list with title + image uri;
myList.ItemsSource = listData;
I need to disable click only for some items depending on some value from MyObj in my list but the others to have it. In Android we use adapter for that, how should I handle it here?
First, you should create a new bool property called Disabled inside your MyObj object.
Then, subscribe to myList's ContainerContentChanging event where you have access to the ListViewItem and its corresponding Item, which in this case is your MyObj. So, if MyObj.Disabled is true, make that ListViewItem non-clickable.
private void myList_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
var listViewItem = args.ItemContainer;
if (listViewItem != null)
{
var model = (MyObj)args.Item;
if (model.Disabled)
{
listViewItem.IsHitTestVisible = false;
// OR
//listViewItem.IsEnabled = false;
}
}
}
Keep in mind that you might want to use listViewItem.IsEnabled = false if you want that item to appear dimmed. This is because the default ListViewItemstyle has a Disabled state that reduces its Opacity; while setting listViewItem.IsHitTestVisible = false won't change its appearance in any way.
The listView is a strange control as it does not have any mechanism to disable selection.
So what I suggest you do is rather handle the event that notifies the framework that an item has been selected by attaching an event handler to ItemSelectionChanged and in there perform a deselect on the item:
yourListView.ItemSelectionChanged += yourListView_ItemSelectionChanged;
private void yourListView_ItemSelectionChanged(
object sender,
ListViewItemSelectionChangedEventArgs e)
{
if (e.IsSelected)
e.Item.Selected = false;
}
Please let me know if my answer helps :)

Change background on one List Item in a listbox (Not the selected item)

I have a ListBox for addresses. Each item is a formatted address label using DataTemplate.
When the user selects an item in the list AND clicks Set to default button, I would like to change the background color of that item to denote the default.
I only want to change that one item, NOT the SelectedItem... so the SelectedItem might be one color and the DEFAULT might be a different color.
I would like to do this pragmatically... even if I need a loop to reset the non-default and set the default...
My problem is that the ListBox.SelectedItem only allows me access to the underlying object in the collection, in this case Address.
So, the following will not work:
foreach (ListBoxItem item in lstShipToAddresses.Items)
{
// does not work (can't cast Address to ListboxItem)
item.Background = Brushes.Magenta;
}
How can I access the background of a particular ListBoxItem?
I have a plan B which involves just using another area outside the ListBox to display the default address, but that would gobble up a bit more screen space, so I'm trying to avoid that.
Update (XAML):
<ListBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Name="lstShipToAddresses"
ItemsSource="{Binding Path=ocShipToAddress}"
SelectionChanged="lstShipToAddresses_SelectionChanged"
SelectedValuePath="Address_ID">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#FF000000" BorderThickness="2,2,2,2" CornerRadius="10" HorizontalAlignment="Stretch" >
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Grid.Row="0" Text="{Binding Path=Address_Label}" HorizontalAlignment="Stretch"></TextBlock>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Final Solution:
This code is done within the button click, so the SelectedItem is the one we want to make default.
for (int i = 0; i < lstShipToAddresses.Items.Count; i++)
{
if (lstShipToAddresses.Items[i] == lstShipToAddresses.SelectedItem)
{
// Set background on default
var listBoxItem = lstShipToAddresses.ItemContainerGenerator.ContainerFromIndex(i);
(listBoxItem as ListBoxItem).Background = Brushes.Magenta;
}
else
{
// Reset background on non-default
var listBoxItem = lstShipToAddresses.ItemContainerGenerator.ContainerFromIndex(i);
(listBoxItem as ListBoxItem).Background = Brushes.White;
}
}
Might need to change the Magenta to a less scary color ;)
You need to use ItemContainerGenerator.ContainerFromIndex for this purpose. It returns a DependencyObject then you can cast it to ListBoxItem and use the ListBoxItem's properties like Background:
for (int i = 0; i < lstShipToAddresses.Items.Count; i++)
{
var listBoxItem = lstShipToAddresses.ItemContainerGenerator.ContainerFromIndex(i);
(listBoxItem as ListBoxItem).Background = Brushes.Magenta;
}

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