Hello I am in need of help to switch the content of two buttons
what I have done so far is to check if the buttons are neighbours.
private int row = 4;
private int col = 4;
public MainWindow()
{
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Button cmd = (Button)sender;
MessageBox.Show(cmd.Tag.ToString());
string txt = cmd.Tag.ToString();
int r = int.Parse("" + txt[0]);
int c = int.Parse("" + txt[1]);
if (Math.Abs(r - row) + Math.Abs(c - col) == 1)
{
MessageBox.Show(r + " " + c);
}
And my buttons in my XAML file is like this
<Button Tag="00" Grid.Row="0" Grid.Column="0" Click="Button_Click">A</Button>
<Button Tag="01" Grid.Row="0" Grid.Column="1" Click="Button_Click">B</Button>
and the Challenge is to switch the content (A and B)
Can anyone help me with that?
do not use WinForms event handling like Button_Click method.
use binding for button content
look at the commands
look at the MVVM approach
Just use a temporary variable to hold one of the strings.
string tmp = Button1.Text;
Button1.Text = Button2.Text;
Button2.Text = tmp;
I'd skip the generic eventhandler name. You need to store the button you want to move to in a temporary variable.
I used nine Buttons:
<Button Tag="00" Grid.Row="0" Grid.Column="0" Margin="15,21,13,23" Name="btn1" Click="btn1_Click">A</Button>
<Button Tag="01" Grid.Row="0" Grid.Column="1" Margin="15,21,13,23" Name="btn2" Click="btn1_Click">B</Button>
<Button Tag="02" Grid.Row="0" Grid.Column="2" Margin="15,21,13,23" Name="btn3" Click="btn1_Click">C</Button>
<Button Tag="10" Grid.Row="1" Grid.Column="0" Margin="15,21,13,23" Name="btn4" Click="btn1_Click">D</Button>
<Button Tag="11" Grid.Row="1" Grid.Column="1" Margin="15,21,13,23" Name="btn5" Click="btn1_Click">E</Button>
<Button Tag="12" Grid.Row="1" Grid.Column="2" Margin="15,21,13,23" Name="btn6" Click="btn1_Click">F</Button>
<Button Tag="20" Grid.Row="2" Grid.Column="0" Margin="15,21,13,23" Name="btn7" Click="btn1_Click">G</Button>
<Button Tag="21" Grid.Row="2" Grid.Column="1" Margin="15,21,13,23" Name="btn8" Click="btn1_Click">H</Button>
<Button Tag="22" Grid.Row="2" Grid.Column="2" Margin="15,21,13,23" Name="btn9" Click="btn1_Click">I</Button>
and This Method to get the Button from the Grid and Change Values:
private int row = 2;
private int col = 2;
private void btn1_Click(object sender, RoutedEventArgs args)
{
Button cmd = (Button)sender;
string txt = cmd.Tag.ToString();
int r = int.Parse("" + txt[0]);
int c = int.Parse("" + txt[1]);
if (Math.Abs(r - row) + Math.Abs(c - col) == 1)
{
MessageBox.Show(r + " " + c);
Button nearButton = grd1.Children.Cast<Button>().First(e => Grid.GetRow(e) == row && Grid.GetColumn(e) == col);
Object tmp = nearButton.Content;
nearButton.Content = cmd.Content;
cmd.Content = tmp;
}
}
In my Example the two Buttons next to the specified change their value with it, hope that is what you intended.
(Getting an Item from a Grid via the X and Y is stolen from here)
Related
I'm trying to build a table in .NET MAUI based on a Grid Layout. This is the code:
<CollectionView ItemsSource="{Binding digitalInputs}">
<CollectionView.Header>
<Grid ColumnDefinitions="*,*,*,*">
<Label Text="Name" Grid.Column="0"/>
<Label Text="Typ" Grid.Column="1"/>
<Label Text="Status" Grid.Column="2"/>
<Label Text="Aktiv" Grid.Column="3"/>
</Grid>
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="services:DigitalInput">
<Grid ColumnDefinitions="*,*,*,*">
<Label Text="{Binding pName}" Grid.Column="0"/>
<Label Text="{Binding pDigitalType}" Grid.Column="1"/>
<Label Text="{Binding pValueText}" Grid.Column="2"/>
<Label Text="{Binding pActive}" Grid.Column="3"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
This is the result when running in Debug Mode on MacCatalyst (Visual Studio for Mac):
Now I wonder how I can align the header grid properly to the grid in the data template? Does someone have a suggestion on how I can improve the code to build a proper table?
Edit: This seems to be a bug in the IDE. When I change the HorizontalOptions property on the Grid in the CollectionView.Header, as a comment suggested, the XAML Hot-Reload triggers a re-rendering of the view and all of a sudden the header grid aligns correctly with the grid in the ItemTemplate.
I tested the code you provided in iOS, Windows in MAUI. And it can align the header grid properly to the grid in the data template in CollectionView. So the issue could be related with the services:DigitalInput retrieving the data, they should be correctly formatted with no blank space in those properties.
Below are the code sample and running output, hope it can shed some light for you!
XAML:
<CollectionView ItemsSource="{Binding digitalInputs}">
<CollectionView.Header>
<Grid ColumnDefinitions="*,*,*,*">
<Label Text="Name" Grid.Column="0"/>
<Label Text="Typ" Grid.Column="1"/>
<Label Text="Status" Grid.Column="2"/>
<Label Text="Aktiv" Grid.Column="3"/>
</Grid>
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate >
<Grid ColumnDefinitions="*,*,*,*">
<Label Text="{Binding pName}" Grid.Column="0"/>
<Label Text="{Binding pDigitalType}" Grid.Column="1"/>
<Label Text="{Binding pValueText}" Grid.Column="2"/>
<Label Text="{Binding pActive}" Grid.Column="3"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Code-behind:
public ObservableCollection<Model> digitalInputs { get; set; }
public NewPage1()
{
InitializeComponent();
//assign data
digitalInputs = new ObservableCollection<Model>()
{
new Model{pName="KipSchalter", pDigitalType="Zustand",pActive="OFF", pValueText="True" },
new Model{pName="KipSchalter", pDigitalType="Zustand",pActive="OFF", pValueText="True" },
new Model{pName="Digital In 3", pDigitalType="Zustand",pActive="OFF", pValueText="FALSE" },
new Model{pName="Digital In 4", pDigitalType="Zustand",pActive="OFF", pValueText="FALSE" }
}
;
BindingContext = this;
}
iOS output:
Windows output:
Update:
This seems to be a potential issue in the IDE. When changing the HorizontalOptions property on the Grid as Jason suggested, the header grid aligns correctly with the grid in the ItemTemplate.
If you mean by arranging children horizontally first and then pushing down to the next row then the current MAUI still does not support that, you can only trigger span (also span isn't changing at runtime on WinUI right now, I think the team is fixing it) or create a custom one
public class HorizontalWrapLayout : StackLayout
{
public HorizontalWrapLayout()
{
}
protected override ILayoutManager CreateLayoutManager()
{
return new HorizontalWrapLayoutManager(this);
}
}
public class HorizontalWrapLayoutManager : StackLayoutManager
{
HorizontalWrapLayout _layout;
public HorizontalWrapLayoutManager(HorizontalWrapLayout horizontalWrapLayout) : base(horizontalWrapLayout)
{
_layout = horizontalWrapLayout;
}
public override Size Measure(double widthConstraint, double heightConstraint)
{
var padding = _layout.Padding;
widthConstraint -= padding.HorizontalThickness;
var rows = new Dictionary<int, List<Size>>();
var currentRowIndex = 0;
var currentRow = new List<Size>();
rows.Add(currentRowIndex, currentRow);
foreach (var child in _layout)
{
if (child.Visibility == Visibility.Collapsed)
{
continue;
}
var childSize = child.Measure(double.PositiveInfinity, heightConstraint);
var childWidth = childSize.Width + (currentRow.Any() ? _layout.Spacing : 0);
var rowWidth = currentRow.Aggregate(0.0, (w, x) => w + x.Width);
if (rowWidth + childWidth > widthConstraint)
{
if (currentRow.Any())
{
currentRowIndex++;
currentRow = new List<Size>();
rows.Add(currentRowIndex, currentRow);
}
}
else if (currentRow.Any())
{
currentRow.Add(new Size(_layout.Spacing, 0));
}
currentRow.Add(childSize);
}
var totalWidth = 0.0;
var totalHeight = 0.0;
if (rows.Any())
{
var rowWidths = rows.Select(x => x.Value.Aggregate(0.0, (result, item) => result + item.Width)).ToList();
var rowHeights = rows.Select(x => x.Value.Any() ? x.Value.Max(i => i.Height) : 0).ToList();
totalWidth = rowWidths.Any() ? rowWidths.Max() : 0;
totalHeight = rowHeights.Any() ? rowHeights.Sum() : 0;
if (rows.Keys.Count > 1)
{
totalHeight += _layout.Spacing * (rows.Keys.Count - 1);
}
}
totalWidth += padding.HorizontalThickness;
totalHeight += padding.VerticalThickness;
var finalHeight = ResolveConstraints(heightConstraint, Stack.Height, totalHeight, Stack.MinimumHeight, Stack.MaximumHeight);
var finalWidth = ResolveConstraints(widthConstraint, Stack.Width, totalWidth, Stack.MinimumWidth, Stack.MaximumWidth);
return new Size(finalWidth, finalHeight);
}
public override Size ArrangeChildren(Rect bounds)
{
var padding = Stack.Padding;
double top = padding.Top + bounds.Top;
double left = padding.Left + bounds.Left;
double currentRowTop = top;
double currentX = left;
double currentRowHeight = 0;
double maxStackWidth = currentX;
for (int n = 0; n < _layout.Count; n++)
{
var child = _layout[n];
if (child.Visibility == Visibility.Collapsed)
{
continue;
}
if (currentX + child.DesiredSize.Width > bounds.Right)
{
// Keep track of our maximum width so far
maxStackWidth = Math.Max(maxStackWidth, currentX);
// Move down to the next row
currentX = left;
currentRowTop += currentRowHeight + _layout.Spacing;
currentRowHeight = 0;
}
var destination = new Rect(currentX, currentRowTop, child.DesiredSize.Width, child.DesiredSize.Height);
child.Arrange(destination);
currentX += destination.Width + _layout.Spacing;
currentRowHeight = Math.Max(currentRowHeight, destination.Height);
}
var actual = new Size(maxStackWidth, currentRowTop + currentRowHeight);
return actual.AdjustForFill(bounds, Stack);
}
Usage
<app:HorizontalWrapLayout
BindableLayout.ItemTemplate="{x:StaticResource HorizontalWrapLayoutItemTemplate}"
BindableLayout.ItemsSource="{Binding ControlGroups, Mode=OneWay}"
HorizontalOptions="Center"
Spacing="50"
VerticalOptions="Center" />
I have a problem, i have 5 buttons that load txt files from system and show it as string on textblocks but i dont know how to do it without 5 event handlers
private void OnClick1(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
numbers1.Text = File.ReadAllText(openFileDialog.FileName);
}
OnClick1 is button1, numbers1 is a textblock1
now i have 5 codes like this (with numbers2.Text, numbers3.Text etc) how can i do it shorter
I may be misunderstanding what you are asking. However, if you want the different buttons to use the same click event. Then the click event is going to have to be able to distinguish “which” button was clicked in order to know which text box to use.
In this case, I recommend you give each button a name and then in the click event, cast the sender as a Button, then check its name to determine which text box to use. All buttons are wired up to this ONE (1) event. Example something like…
private void btn_Click(object sender, RoutedEventArgs e) {
Button btnSender = (Button)sender;
TextBox tb = null;
switch (btnSender.Name) {
case "btn1":
tb = txt1;
break;
case "btn2":
tb = txt2;
break;
case "btn3":
tb = txt3;
break;
case "btn4":
tb = txt4;
break;
}
if (tb != null) {
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
tb.Text = File.ReadAllText(openFileDialog.FileName);
}
}
This should get you started I think
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = true;
List<TextBlock> textBlocks = new List<TextBlock>();
textBlocks.Add(txt1);
textBlocks.Add(txt2);
textBlocks.Add(txt3);
textBlocks.Add(txt4);
textBlocks.Add(txt5);
int count = 0;
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
foreach (String files in openFileDialog.FileNames)
{
var currentText = textBlocks[count];
currentText.Text = File.ReadAllText(files);
count++;
}
}
}
Here is the xaml code
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="5">
<Button Name="btnLoad" Content="Load All" Click="btnLoad_Click" ></Button>
<TextBlock x:Name="txt1" HorizontalAlignment="Left" Margin="5" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="118"/>
<TextBlock x:Name="txt2" HorizontalAlignment="Left" Margin="5" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="118"/>
<TextBlock x:Name="txt3" HorizontalAlignment="Left" Margin="5" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="118"/>
<TextBlock x:Name="txt4" HorizontalAlignment="Left" Margin="5" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="118"/>
<TextBlock x:Name="txt5" HorizontalAlignment="Left" Margin="5" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="118"/>
</StackPanel>
i want to get in my codebehind the Content of an button that has a grid in it with multiple textboxes.
i had this before Code and this works:
XAML:
<Button Click="btnClick_upload_Data">
<Button.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="test1" ></TextBlock>
<TextBlock Text="test2" ></TextBlock>
</StackPanel>
</Button.Content>
</Button>
codebehind:
private void btnClick_upload_Data(object sender, RoutedEventArgs e)
{
string s = ((((sender as Button).Content) as StackPanel).Children[1] as TextBlock).Text;
//…
and this way i got the "test2" im my string variable.
now my XAML has changed a bit
my Question is how do i have to Change my function so i still get "test2" in my string variable 's'
new XAML:
<Button Click="btnClick_upload_Data" >
<Button.Content>
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="test1" Grid.Row="0"></TextBlock>
<TextBlock Text="test2" Grid.Row="1"></TextBlock>
</Grid>
</StackPanel>
</Button.Content>
</Button>
new XAML:
private void btnClick_upload_Data(object sender, RoutedEventArgs e)
{
//????
thanks in advance
Try this:
private void btnClick_upload_Data(object sender, RoutedEventArgs e)
{
string s = null;
Button btn = (Button)sender;
StackPanel sp = btn.Content as StackPanel;
if (sp != null && sp.Children.Count > 0)
{
Grid grid = sp.Children[0] as Grid;
if (grid != null && grid.Children.Count > 1)
{
TextBlock textBlock = grid.Children[1] as TextBlock;
if (textBlock != null)
s = textBlock.Text;
}
}
MessageBox.Show(s);
}
I really want to create my own map control in WPF because the only one that would be suitable for my requirements would be the Google Maps JavaScript API because it can do almost anything I need but that is only for web and mobile and I have tried the Bing and ESRI maps but they can't do what I want.
I have started a little experiment project to see if I can load the tiles when zooming and it kind of works, here is the code:
<ScrollViewer Margin="10" PanningMode="Both" HorizontalScrollBarVisibility="Visible">
<Canvas x:Name="lyrTiles" Height="3000" Width="3000"/>
</ScrollViewer>
<Grid x:Name="lyrControl" Margin="10">
<Button x:Name="moveUp" Content="U" HorizontalAlignment="Left" Margin="35,10,0,0" VerticalAlignment="Top" Width="20" Height="20"/>
<Button x:Name="moveRight" Content="R" HorizontalAlignment="Left" Margin="55,30,0,0" VerticalAlignment="Top" Width="20" Height="20"/>
<Button x:Name="moveDown" Content="D" HorizontalAlignment="Left" Margin="35,50,0,0" VerticalAlignment="Top" Width="20" Height="20"/>
<Button x:Name="moveLeft" Content="L" HorizontalAlignment="Left" Margin="15,30,0,0" VerticalAlignment="Top" Width="20" Height="20"/>
<Button x:Name="zoomIn" Content="ZI" HorizontalAlignment="Left" Margin="35,81,0,0" VerticalAlignment="Top" Width="20" Height="20"/>
<Button x:Name="zoomOut" Content="ZO" HorizontalAlignment="Left" Margin="35,311,0,0" VerticalAlignment="Top" Width="20" Height="20"/>
<Slider x:Name="zoomSlider" HorizontalAlignment="Left" Margin="35,106,0,0" VerticalAlignment="Top" Orientation="Vertical" Height="200" Width="20" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Interval="1"/>
<Button x:Name="typeHybrid" Content="Hybrid" HorizontalAlignment="Right" Margin="0,10,65,0" VerticalAlignment="Top" Width="50" Height="15" Padding="0,-1,0,0" FontSize="10"/>
<Button x:Name="typeTerrain" Content="Terrain" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top" Width="50" Height="15" Padding="0,-1,0,0" FontSize="10"/>
<Button x:Name="typeSatellite" Content="Satellite" HorizontalAlignment="Right" Margin="0,10,120,0" VerticalAlignment="Top" Width="50" Height="15" Padding="0,-1,0,0" FontSize="10"/>
<Button x:Name="typeRoad" Content="Road" HorizontalAlignment="Right" Margin="0,10,175,0" VerticalAlignment="Top" Width="50" Height="15" Padding="0,-1,0,0" FontSize="10"/>
<Label x:Name="copyright" Content="Map data ©2014 Google" HorizontalAlignment="Right" VerticalAlignment="Bottom" Padding="0" Width="200" FontSize="10" FontFamily="Calibri" FontWeight="Bold"/>
</Grid>
<Canvas x:Name="lyrActive" Margin="10,10,27,28" MouseWheel="lyrActive_MouseWheel" Background="#00000000"/>
public int zoomLevel = 0;
public int zoomWidth = 2;
public MainWindow()
{
InitializeComponent();
Image i = new Image(); i.Width = 250; i.Height = 250; i.Margin = new Thickness(0, 0, 0, 0);
i.Source = new BitmapImage(new Uri("https://a.tiles.mapbox.com/v3/examples.map-9ijuk24y/0/0/0.png"));
lyrTiles.Children.Add(i);
}
private void lyrActive_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0)
{
lyrTiles.Children.Clear();
zoomLevel += 1; zoomWidth = zoomWidth + zoomWidth / 2;
for (int x = 0; x < zoomWidth; x++)
{
for (int y = 0; y < zoomWidth; y++)
{
lyrTiles.Children.Add(new Image()
{
Margin = new Thickness(250 * x, 250 * y, 0, 0),
Source = new BitmapImage(new Uri("https://a.tiles.mapbox.com/v3/examples.map-9ijuk24y/" + zoomLevel + "/" + x + "/" + y + ".png"))
});
}
}
}
}
private void ScrollViewer_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0)
{
lyrTiles.Children.Clear();
zoomLevel += 1; zoomWidth = zoomWidth + zoomWidth / 2;
for (int x = 0; x < zoomWidth; x++)
{
for (int y = 0; y < zoomWidth; y++)
{
lyrTiles.Children.Add(new Image()
{
Margin = new Thickness(250 * x, 250 * y, 0, 0),
Source = new BitmapImage(new Uri("https://a.tiles.mapbox.com/v3/examples.map-9ijuk24y/" + zoomLevel + "/" + x + "/" + y + ".png"))
});
}
}
}
}
Is this the correct way I should be rendering the tiles? I know I have to remove ones that are not visible but this is just a very very simple project to see what I can actually do to make a map. How can I start to make this work better?
Also, I think that the biggest and most important thing will be the coordinates because they are used from everything from locating the centre of the map so it can download the correct tiles to placing markers at specific locations. How can I do this, do I need some sort of huge latitude and longitude axis?
Here's a number of projects that might save you some work:
OpenSource:
http://www.codeproject.com/Articles/238551/WPF-Map-App-WPF-meets-Google-Geocoding-Static-Maps
http://xamlmapcontrol.codeplex.com/
http://greatmaps.codeplex.com/
http://wpfsharpmapcontrols.codeplex.com/
Commercial:
http://thinkgeo.com/map-suite-developer-gis/wpf-edition/
http://resources.arcgis.com/en/help/runtime-wpf/concepts/index.html#//017000000031000000
https://www.carmenta.com/en/products/carmenta-engine/overview
http://www.componentsource.com/products/componentart-maps-for-wpf/index-gbp.html
If you don't mind using Bing:
http://msdn.microsoft.com/en-us/library/hh750210.aspx
http://visualstudiomagazine.com/Articles/2012/04/01/Map-Your-Apps.aspx?Page=1
I try to clear (and add) items to ListPicker, but when the app have to clear all of the items is an error - "SelectedItem must always be set to a valid value". My code:
<toolkit:ListPicker x:Name="select" HorizontalAlignment="Right" Margin="0,135,30,0" VerticalAlignment="Top" Width="195" Height="64" d:LayoutOverrides="HorizontalAlignment" BorderBrush="{x:Null}" FontFamily="Arial" FontWeight="Bold" FontSize="32" Style="{StaticResource ListPickerStyle1}" BorderThickness="0" DataContext="{Binding}">
<toolkit:ListPicker.Background>
<ImageBrush Stretch="Fill" ImageSource="list_picker.png"/>
</toolkit:ListPicker.Background>
and action for a button
private void button_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
select.Items.Clear(); //here is an error
for (int i = 0; i < arrays.Length; i++)
{
select.Items.Add(arrays[i]);
}
}
I try another options, but it doesn't work too.
private void button_Tap(object sender, System.Windows.Input.GestureEventArgs e)
select.SelectedItem = null; // here is an error
select.Items.Clear();
for (int i = 0; i < arrays.Length; i++)
{
select.Items.Add(arrays[i]);
}
}
To clear the selection you should use:
select.SelectedItems.Clear();