Hi,
erst einmal vielen Dank für das Tutorial.
Bei den Übungen ist mir bei der Aufgabe 10 (Zahl zwischen -750 und 750) aufgefallen, dass das Pattern aus der Lösung auch -000, -00, -0, 00 und 000 zulässt. Wenn statt
^-?(7[0-4]\d|750|[0-6]?\d{1,2})$ das Pattern
^(0$|-?([1-9]\d?|[1-6]\d{0,2}|7[0-4]\d|750))$ verwendet wird besteht das Problem nicht. Sprich es wird geprüft, ob die Eingabe = 0 oder Minus bzw. kein Zeichen und 1-99 oder 100-699 oder 700-749 oder 750 ist.
Ähnlich ist es bei Aufgabe 11 (dreistellige Zahl von -750 bis 750). Hier funktionieren auch -000 und 000.
Das Problem lässt sich beheben indem aus
^-?(7[0-4]\d|750|[0-6]\d{2})$
^-?(7[0-4]\d|750|[1-6]\d{2})$ wird.
Dann ist mir noch ein Schreibfehler im Abschnitt 4.13 Positive und negative Look arounds aufgefallen:
Lookarounds (auch Assertations genannt) stellen die die Leistungsfähigkeit von
Vielen Dank für deine Lösung.
Um die 8x8 Pixel einzustellen reicht es beispielweise einmal den Debugmodus im VS zu starten und einen Rechtsklick auf die Titelleiste der Konsole zu machen und die Eigenschaften entsprechend anzupassen. Die werden gespeichert und bleiben beim nächsten Start bestehen. Aber das ist nur ein Detail, dass jeder selbst einstellen kann und auch nicht in der Aufgabenstellung gefordert war.
Ansonsten habe ich mir die Umsetzung genau so vorgestellt, auch der Quellcode ist schön übersichtlich geworden. Wenn du die DrawRombus-Methode noch entfernst ist er noch ein wenig kürzer 😉. Die Aufgabe ist damit gelöst und du darfst die nächste stellen.
Das Ziel die Aufgabe besteht darin eine Morphing-Animation zweier geometrischer Figuren auf der Konsole zu erstellen. Dabei soll ein zeitgesteuerter gleichmäßiger Übergang von einem Quadrat zu einem Rhombus mit gleichlangen Diagonalen erzeugt werden. Die Länge der Diagonalen im Rhombus ist gleich der Seitenlänge des Quadrates. Um das zu verdeutlichen habe ich ein Bild angehangen.
Zu Beginn ist die blaue Form zu sehen, welche in die rote Form übergeht. Nachdem die Endform erreicht ist, soll die Animation rückwärts ablaufen, bis wieder die ursprüngliche Form erreicht ist. Die Animation läuft unendlich lange.
Zusätzlich ist zu beachten, dass die Animation für verschieden große Quadrate funktionieren soll und dass zu jeder Zeit der Animation eine vollständig umschlossene Form sichtbar ist, die zur x- und y- Achse symmetrisch ist. Die Farbe der Form ist beliebig.
Es empfiehlt sich in der Konsole Zeichen gleicher Höhe und Breite zu verwenden. Zum zeichnen einer Linie kann die untenstehende Methode verwendet werden.
public void drawLine(int x1, int y1, int x2, int y2, ConsoleColor bgColor)
{
ConsoleColor BackgroundColorTemp = Console.BackgroundColor;
int cursorLeftTemp = Console.CursorLeft;
int cursorTopTemp = Console.CursorTop;
Console.BackgroundColor = bgColor;
int dx = Math.Abs(x2 - x1), sx = x2 < x1 ? -1 : 1;
int dy = -Math.Abs(y2 - y1), sy = y2 < y1 ? -1 : 1;
int err = dx + dy, e2;
while(true)
{
Console.CursorLeft = x1;
Console.CursorTop = y1;
Console.Write(" ");
if(x1 == x2 && y1 == y2) break;
e2 = 2 * err;
if(e2 > dy) { err += dy; x1 += sx; }
if(e2 < dx) { err += dx; y1 += sy; }
}
Console.BackgroundColor = BackgroundColorTemp;
Console.CursorLeft = cursorLeftTemp;
Console.CursorTop = cursorTopTemp;
}
Ich hoffe du hast den Spaß noch nicht verloren, sondern siehst es im Gegenteil als spannend und interessant an, wie viele Fehler in einer auf den ersten Blick korrekten Implementierung eines an sich überschaubaren Algorithmus stecken können, wenn man sehr genau hinschaut.
An dieser Aufgabe sieht man sehr schön wie gemein die "Einschränkung" alle decimal Zahlen (bzw. die die sich mit Math.Abs als positive umwandeln lassen) ist, insbsondere wenn jemand so genau wie du prüft. Der Spaß ist aber noch nicht weg.
Ich betrachte significantDigits > 29 jetzt auch als Argumentfehler. Schon im letzten Lösungsvorschlag wurde PadRight nur noch verwendet, wenn d = 0 ist. Für alle anderen Fälle hatte die ToString-Methode schon den richtigen Wert ausgegeben. Wenn die Zahl wie in deinem Beispiel statt 29 nur 28 signifikante Stellen hat, hänge ich auch keine Null mehr an, da die letzte Stelle des decimals schon von anfang an gerundet sein kann. Darüber lässt sich sicherlich auch streiten. Es wäre aber auch kein Problem das anfügen der Nullen wieder wie in früheren Vorschlägen durchzuführen.
public static string FormatSI(Decimal d, int significantDigits, String unit)
{
string sign = "";
int powerOfThousand;
string textOfNumber;
string siPrefix;
if(significantDigits < 1)
throw new ArgumentException("Der Parameter \"significantDigits\" muss größer als 0 sein.");
if(significantDigits > 29)
throw new ArgumentException("Der Parameter \"significantDigits\" darf nicht größer als 29 sein.");
if(d < 0)
{
sign = "-";
d = Math.Abs(d);
}
powerOfThousand = getPowerOfThousand(d);
d = scaleNumber(d);
d = roundSignificant(d, significantDigits);
// range overflow
if(!(d < 1000m))
{
d = scaleNumber(d);
d = roundSignificant(d, significantDigits);
powerOfThousand++;
}
textOfNumber = getSIText(d, significantDigits);
siPrefix = getSIPrefix(powerOfThousand);
return sign + textOfNumber + siPrefix + unit;
}
private static int getPowerOfThousand(decimal d)
{
int power;
scaleNumberAndGetPower(d, 1000, out power);
return power;
}
private static int getPowerOfTen(decimal d)
{
int power;
scaleNumberAndGetPower(d, 10, out power);
return power;
}
private static decimal scaleNumber(decimal d)
{
int power;
return scaleNumberAndGetPower(d, 1000, out power);
}
private static decimal scaleNumberAndGetPower(decimal d, int upperBound, out int power)
{
decimal factor = (d < 1) ? upperBound : 1m / upperBound;
int indexStep = (d < 1) ? -1 : +1;
int expontent = 0;
if(d != decimal.Zero)
{
while(d < 1 || d >= upperBound)
{
d *= factor;
expontent += indexStep;
}
}
power = expontent;
return d;
}
private static decimal roundSignificant(decimal d, int significantDigits)
{
if(d.Equals(decimal.Zero))
return d;
try
{
decimal scaleFactor = (decimal)Math.Pow(10, getPowerOfTen(d) - significantDigits + 1);
decimal a = d / scaleFactor;
decimal b = Math.Round(a, MidpointRounding.AwayFromZero);
return b * scaleFactor;
}
catch(OverflowException)
{
return roundSignificant(d, --significantDigits);
}
}
private static string getSIText(decimal d, int significantDigits)
{
string textOfNumber = d.ToString();
if(d == decimal.Zero && significantDigits > 1)
{
string decimalSeparator = System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
int requiredLength = significantDigits + 1;
textOfNumber += ",";
textOfNumber = textOfNumber.PadRight(requiredLength, '0');
}
return textOfNumber;
}
private static string getSIPrefix(int powerOfThousand)
{
string[] prefixes = { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" };
if(powerOfThousand <= -9 || powerOfThousand >= 9)
return "E" + powerOfThousand * 3;
int offset = prefixes.Length / 2;
return prefixes[powerOfThousand + offset];
}
Nenn mich pingelig 😃 aber ich hatte direkt in der Aufgabe ausdrücklich gefordert, dass "die Methode für jede mögliche Decimal-Zahl wie beschrieben funktionieren soll".
Ist schon richtig, wenn die Methode nicht den Anforderungen entspricht, dann muss sie korrigiert werden. Ich frage mich aber warum nicht alle Methoden der Math Bibliothek auch für decimal implementiert sind.
Insbesondere wird Math.Round möglicherweise nicht so runden, wie es ToString tut, siehe z.B. Math.Round rundet anders als erwartet.
Gut zu wissen, ich bin bisher davon ausgegangen, dass Math.Round standardmäßig kaufmännisch rundet
Und jetzt zum eigentlichen Anliegen. Aller guten Dinge sind (hoffentlich) drei, hier ist meine überarbeitete Version:
public static string FormatSI(Decimal d, int significantDigits, String unit)
{
string sign = "";
int powerOfThousand;
string textOfNumber;
string siPrefix;
if(significantDigits < 1)
throw new ArgumentException("der Parameter significantDigits muss größer als 0 sein.");
if(d < 0)
{
sign = "-";
d = Math.Abs(d);
}
powerOfThousand = getPowerOfThousand(d);
d = scaleNumber(d);
d = roundSignificant(d, significantDigits);
// range overflow
if(!(d < 1000m))
{
d = scaleNumber(d);
d = roundSignificant(d, significantDigits);
powerOfThousand++;
}
textOfNumber = getSIText(d, significantDigits);
siPrefix = getSIPrefix(powerOfThousand);
return sign + textOfNumber + siPrefix + unit;
}
private static int getPowerOfThousand(decimal d)
{
int power;
scaleNumberAndGetPower(d, 1000, out power);
return power;
}
private static int getPowerOfTen(decimal d)
{
int power;
scaleNumberAndGetPower(d, 10, out power);
return power;
}
private static decimal scaleNumber(decimal d)
{
int power;
return scaleNumberAndGetPower(d, 1000, out power);
}
private static decimal scaleNumberAndGetPower(decimal d, int upperBound, out int power)
{
decimal factor = (d < 1) ? upperBound : 1m / upperBound;
int indexStep = (d < 1) ? -1 : +1;
int expontent = 0;
if(d != decimal.Zero)
{
while(d < 1 || d >= upperBound)
{
d *= factor;
expontent += indexStep;
}
}
power = expontent;
return d;
}
private static decimal roundSignificant(decimal d, int significantDigits)
{
if(d.Equals(decimal.Zero))
return d;
decimal scaleFactor = (decimal)Math.Pow(10, getPowerOfTen(d) - significantDigits + 1);
// scale to significant digits
decimal a = d / scaleFactor;
decimal b = Math.Round(a, MidpointRounding.AwayFromZero);
// scale back
return b * scaleFactor;
}
private static string getSIText(decimal d, int significantDigits)
{
string textOfNumber = d.ToString();
if(d == decimal.Zero && significantDigits > 1)
{
string decimalSeparator = System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
int requiredLength = significantDigits + 1;
textOfNumber += ",";
textOfNumber = textOfNumber.PadRight(requiredLength, '0');
}
return textOfNumber;
}
private static string getSIPrefix(int powerOfThousand)
{
string[] prefixes = { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" };
if(powerOfThousand <= -9 || powerOfThousand >= 9)
return "E" + powerOfThousand * 3;
int offset = prefixes.Length / 2;
return prefixes[powerOfThousand + offset];
}
Die letzten Tage hatte ich wenig Zeit, aber heute bin ich wieder dazu gekommen die Methode zu überarbeiten:
public static string FormatSI(Decimal d, int significantDigits, String unit)
{
string sign = "";
int thousandsExponent = 0;
if(significantDigits < 1)
throw new ArgumentException("der Parameter significantDigits muss größer als 0 sein.");
if(d < 0)
{
sign = "-";
d = Math.Abs(d);
}
d = scaleNumberAndGetSIPrefix(d, out thousandsExponent);
d = round(d, significantDigits);
// range overflow
if(!(d < 1000m))
{
d = scaleNumber(d);
d = round(d, significantDigits);
thousandsExponent++;
}
return sign + getSIText(d, significantDigits) + getSIPrefix(thousandsExponent) + unit;
}
private static decimal scaleNumberAndGetSIPrefix(decimal d, out int thousandsExponent)
{
decimal multiplicationFactor = (d < 1) ? 1000m : 0.001m;
int indexStep = (d < 1) ? -1 : +1;
int expontent = 0;
if(d != decimal.Zero)
{
while(d < 1 || d >= 1000)
{
d *= multiplicationFactor;
expontent += indexStep;
}
}
thousandsExponent = expontent;
return d;
}
private static decimal scaleNumber(decimal d)
{
int num;
return scaleNumberAndGetSIPrefix(d, out num);
}
private static decimal round(decimal d, int significantDigits)
{
if(d.Equals(decimal.Zero))
return d;
double scaleExponent = Math.Floor(Math.Log10((double)d) - significantDigits + 1);
decimal scaleFactor = (decimal)Math.Pow(10, scaleExponent);
// scale to significant digits
decimal a = d / scaleFactor;
decimal b = Math.Round(a);
// scale back
return b * scaleFactor;
}
private static string getSIText(decimal d, int significantDigits)
{
string textOfNumber = d.ToString();
string decimalSeparator = System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
bool hasDecimalSeparator = textOfNumber.Contains(decimalSeparator);
int requiredLength = significantDigits;
if(hasDecimalSeparator)
requiredLength++;
if(textOfNumber.Length < requiredLength)
{
if(!hasDecimalSeparator)
{
textOfNumber += ",";
requiredLength++;
}
textOfNumber = textOfNumber.PadRight(requiredLength, '0');
}
return textOfNumber;
}
private static string getSIPrefix(int thousandsIndex)
{
string[] prefixes = { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" };
if(thousandsIndex <= -9 || thousandsIndex >= 9)
return "E" + thousandsIndex * 3;
int offset = prefixes.Length / 2;
return prefixes[thousandsIndex + offset];
}
Hier ist mein Lösungsvorschlag.
Beim Runden auf die signifikanten Stellen, wollte ich keine eigene Methode schreiben, sondern die Mittel von .Net nutzen. Prinzipiell kann man das beim Umwandeln einer Zahl in eine Zeichenkette mit Hilfe des Formatzeichens G bzw. g lösen. Dabei wird bei der Ausgabe aber immer die kompakteste Schreibweise ausgegeben, so dass unter Umständen nicht die Komma-Schreibweise, sondern wissenschaftliche Notation ausgegeben wird, bsp. aus 123 wird bei 2 Signifkanten Stellen 1E+02. Daher parse ich in diesen Fällen die Zahl erneut als decimal und wandle sie wieder in einen String um. Ist etwas getrickst, aber funktioniert. Wenn jemand eine Idee hat wie man das schöner lösen kann, bzw. allgemeine Verbesserungsvorschläge hat, dann immer her damit. 😉
public static string FormatSI(Decimal d, int significantDigits, String unit)
{
string sign = "";
string TextOfNumber = "";
string siPrefix = "";
if(significantDigits < 1)
throw new ArgumentException("der Parameter significantDigits muss größer als 0 sein.");
if(d < 0)
{
sign = "-";
d = Math.Abs(d);
}
d = scaleNumberAndGetSIPrefix(d, out siPrefix);
TextOfNumber = getSIString(d, significantDigits);
return sign + TextOfNumber + siPrefix + unit;
}
private static decimal scaleNumberAndGetSIPrefix(decimal d, out string prefix)
{
decimal multiplicationFactor = (d < 1) ? 1000m : 0.001m;
int indexStep = (d < 1) ? -1 : +1;
int thousandsIndex = 0;
if(d != decimal.Zero)
{
while(d < 1 || d >= 1000)
{
d *= multiplicationFactor;
thousandsIndex += indexStep;
}
}
prefix = getSIPrefix(thousandsIndex);
return d;
}
private static string getSIPrefix(int thousandsIndex)
{
string[] prefixes = { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" };
if(thousandsIndex <= -9 || thousandsIndex >= 9)
return "E" + thousandsIndex * 3;
int offset = prefixes.Length / 2;
return prefixes[thousandsIndex + offset];
}
private static string getSIString(decimal d, int significantDigits)
{
// round to significant digits
string textOfNumber = d.ToString("G" + significantDigits);
if(textOfNumber.ToLower().Contains('e'))
{
// exponential notation to decimal point notation
textOfNumber = decimal.Parse(textOfNumber, System.Globalization.NumberStyles.Float).ToString();
}
bool hasDecimalPoint = textOfNumber.Contains(',') || textOfNumber.Contains('.');
int requiredLength = significantDigits;
if(hasDecimalPoint)
requiredLength++;
// pad with zeros if necessary
if(textOfNumber.Length < requiredLength)
{
if(!hasDecimalPoint)
{
textOfNumber += ",";
requiredLength++;
}
textOfNumber = textOfNumber.PadRight(requiredLength, '0');
}
return textOfNumber;
}
Du kannst auch eine generische Methode erstellen, so dass die Methode dir gleich das richtige Objekt zurück gibt.
settingList = Serializer.deserializeObject<List<Setting>>();
die Methode dazu sieht dann in etwa so aus
public static T deserializeObject<T>()
{
...
return (T)formatter.Deserialize(stream);
}
DP können nur in Klassen eigesetzt werden welche von DependencyObject erben, da ansonsten GetValue und SetValue fehlen. Damit haben sie in einer ViewModel Klasse nichts verloren.
Im ersten Beitrag steht nicht, dass es im Zusammenhang mit MVVM verwendet wird. Ich wollte die DPs auch nur als weitere Möglichkeit nennen.
Es gibt MVVM-Frameworks wie Cinch bei denen es über Lambda-Expressions geht.
gut zu wissen
Man könnte INotifyPropertyChanged einsetzen oder eine dependency property erstellen.
Hier mal ein Beispiel für INotifyPropertyChanged:
public class myClass : INotifyPropertyChanged
{
private int var;
public event PropertyChangedEventHandler PropertyChanged;
public int Var
{
get { return var; }
set
{
var = value;
OnChanged("Var");
}
}
private void OnChanged(string propertyName) {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Die OnChanged-Methode vereinfacht, das Auslösen von PropertyChanged, wenn man viele Properties hat. Unschön finde ich, dass man den Namen, der Eigenschaft als Zeichenkette angeben muss, aber ich kenne keine andere Lösung dafür.
Hat denn niemand eine Idee?
Was ich noch vergessen hatte zu sagen ist, dass ich bei dem SelectedValueBinding die UpdateSourceTrigger Eigenschaft auf LostFocus setzen muss, da sonst der ausgewählte Wert nie angezeigt wird.
Es gibt aber eine neue Erkenntnis.
Ich habe nun einen ObjectDataProvider eingesetzt und diesen als ItemsSource in der Standard DataGridComboBoxColumn von WPF verwendet. Wenn ich in eine neue Zeile klicke kommt nun nicht mehr der Fehler "Der Wert "" konnte nicht konvertiert werden". In der ValidationRule kann ich immer noch auf das ausgewählte Objekt zugreifen, jedoch hat die Eigenschaft, die angezeigt wird noch den alten Wert.
Das Problem mit dem roten Rahmen konnte ich beheben.
Problem war, dass ich im DataGrid das SelectedItem an ein Objekt gebunden hatte.
Lösung: das SelectedItem muss an eine Property vom Typ Object, also kein erbendes Object gebunden werden. Finde ich zwar nicht schön, aber es geht.
MfG Ace
Ich verwende auch MVVM und habe die Lösung von TrippleX umgesetzt, jedoch gibt es das gleiche Problem wie bei ChrDressler und dass ich beim Validieren der Zeile nicht auf den ausgewählten Wert zugreifen kann. Es gibt folgende optische auswirkungen:
Ich habe mir ein Error Template erstellt damit ich die Fehlermeldung lesen kann, dort steht: "Der Wert "" konnte nicht konvertiert werden". Einen Konverter gibt es aber nicht. WPF wird wohl im Hintergrund irgendetwas automatisch machen.
Diese Erkenntnisse habe ich schon gewonnen:
Unter dem Link von TrippleX findet man auch die Solution vom Originalbeispiel. Diese ist mit .Net 3.5 erstellt worden. Wenn ich dort im DataGrid eine neue Zeile hinzufüge funktioniert alles. Wenn ich die Solution jedoch auf .Net 4.0 umstelle und alle Kompilierungsfehler behebe, habe ich die gleichen Auswirkungen wie oben.
Zum Validieren habe ich mir eine ValidationRule erstellt. Diese habe ich einer BindingGroup zugeordnet, welche wiederrum über die ItemBindingGroup Eigenschaft dem DataGrid zugewiesen wird. Das Funktioniert soweit auch.
Problem ist aber folgendes, wenn ich aus der ComboBox einen anderen Eintrag auswähle (egal, ob in einer bestehender Zeilen oder einer neuen Zeile), dann kann ich über bindingGroup.items auch auf alle aktuellen Werte in der Zeile zugreifen. Einzige ausnahme ist hier der Text, der aus der ComboBox ausgewählt wurde, dieser enthält noch den Wert vor der aktualisierung (bei einer neuen Zeile also ""), aber auf diesen Wert muss ich zur Validierung auch zugreifen können.
Also nächster Versuch: Zugriff über die getValue Methode der BindingGroup. Als Belohnung kommt eine ValueUnavailableException. Alle anderen Eigenschaften des Objekts kann ich aber über beide Weisen auslesen. Nochmal zur Verdeutlichung ein Code Beispiel (Name wird in der ComboBox angezeigt):
...
// in ValidationRule-Klasse
myObject row = bindingGroup.items[0] as myObject
//Versuch 1: gibt Wert vor Aktualisierung zurück
Console.WriteLine(row.Name);
//Versuch 2: ValueUnavailableException, Bindungsgruppe enthält keine Bindung, die das Element "myObject und die Name-Eigenschaft verwendet.
Console.WriteLine(bindingGroup.GetValue(row,"Name");
...
Ich habe auch schon den ValidationStep auf UpdatedValue getstellt und das SelectedValueBinding der ComboBox auf updateSourceTrigger=LostFocus
Ich versuche das schon stundenlang zum laufen zu bekommen, aber egal was ich auch versuche es will einfach nicht funktionieren. Es muss doch irgendeine einfache Lösung geben, einer DataGridComboBoxColumn eine ItemSource aus dem DataContext zuzuweisen, die vorallem auch in allen Situationen funktioniert.
Hallo,
ich entwickle gerade ein kleines Programm, das später mal auf Terminal-Servern laufen soll und auf WPF basiert. Nun bin ich über ein (hoffentlich nicht großes) Problem im Zusammenhang von WPF auf einem TS gestolpert. Ich habe gelesen, dass es aufgrund des Renderings über DirectX zu Performance-Problemen auf dem Terminal-Server kommen kann.
Nach Internet-Recherche ist mir aber noch unklar inwiefern sich die Probleme bemerkbar machen oder ob diese eventuell auch nur bei Anwendungen mit grafisch aufwendigen Spielerein (z.B. 3D Animationen) auftreten.
In meiner Anwendung wird es auf jeden Fall keine besonderen grafischen Effekte geben. Wie sind eure Erfahrungen zu diesem Thema?
Ich habe nun doch eine Lösung für das Problem gefunden. Das Problem ist einfach, dass die Methoden der Interop keinen Rückgabewert haben. Da VBA "erfreulicherweise" zwischen Funktionen mit und ohne Rückgabewert unterscheidet, muss man diese im VBA entweder mit call aufrufen oder die Klammern bei den Parametern weglassen. Warum der Aufruf der ersten Funktion trotzdem funktioniert hat, weiß ich nicht, aber scheinbar ist VBA an dieser Stelle tolerant.
Jetzt weiß ich wieder warum ich VBA nicht mag.
Hallo,
ich möchte eine Erweiterung für die Sage Office Line in .Net programmieren. Da die OL auf Access basiert, denke ich passt dieses Unterforum am besten.
Zu meinem Problem: Die OL stellt eine Reihe von Objekten über DLLs zur Verfügung. Ich versuche gerade das Mandanten-Objekt aus einem Access Addin an ein .Net Projekt zu übergeben, was einfach nicht funktionieren will.
Ich habe mir zum Test eine COM-Interop in C# geschrieben, im .Net Framework 4.0. Hier der Quellcode:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Importtool.Interop
{
[Guid("C89D342D-7E41-42BF-867C-62259988C206")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IImport
{
[DispId(1)]
void ShowImporttool(int Mandant);
[DispId(2)]
void ShowImporttool(OLSysIInterop60.Mandant Mandant);
[DispId(3)]
void ShowImporttool(Sagede.OfficeLine.Engine.Mandant Mandant);
[DispId(4)]
void ShowImporttool(Sagede.OfficeLine.Interop60.Mandant Mandant);
}
[Guid("E2C4C00C-2950-4EC7-92A0-F2675153FF08")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("ComInterOpClass")]
public class Importtool : IImport
{
[STAThread]
public void ShowImporttool(int Mandant)
{
MainWindow win = new MainWindow();
win.setMandant(Mandant);
win.Show();
}
[STAThread]
public void ShowImporttool(OLSysIInterop60.Mandant Mandant)
{
MainWindow win = new MainWindow();
win.setMandant(Mandant);
win.Show();
}
[STAThread]
public void ShowImporttool(Sagede.OfficeLine.Engine.Mandant Mandant)
{
MainWindow win = new MainWindow();
win.setMandant(Mandant);
win.Show();
}
[STAThread]
public void ShowImporttool(Sagede.OfficeLine.Interop60.Mandant Mandant)
{
MainWindow win = new MainWindow();
win.setMandant(Mandant);
win.Show();
}
}
}
Wie gesagt es ist erstmal nur ein Test. MainWindow ist eine WPF Anwendung die gestartet werden soll, was auch soweit funktioniert. Im Access habe ich mir simples Formular mit 4 Buttons erstellt, die die einzenlen Funktionen aufrufen:
Private Sub Befehl1_Click()
Dim frmImport As Importtool_Interop.Importtool
Set frmImport = New Importtool_Interop.Importtool
frmImport.ShowImporttool (goMandant.nID)
End Sub
Private Sub Befehl2_Click()
Dim frmImport As Importtool_Interop.Importtool
Set frmImport = New Importtool_Interop.Importtool
frmImport.ShowImporttool_2 (goMandant)
End Sub
Private Sub Befehl3_Click()
Dim frmImport As Importtool_Interop.Importtool
Set frmImport = New Importtool_Interop.Importtool
frmImport.ShowImporttool_3 (goMandant)
End Sub
Private Sub Befehl4_Click()
Dim frmImport As Importtool_Interop.Importtool
Set frmImport = New Importtool_Interop.Importtool
frmImport.ShowImporttool_4 (goMandant)
End Sub
Leider Funktioniert nur der erste Aufruf. Diesen habe ich eingebaut um die generelle Funktionsweise zu prüfen. Die ID des Mandanten allein nützt mir aber noch nicht so viel, ich benötige das gesamte Objekt. Bei den anderen 3 Aufrufen kommt immer folgende Meldung:
Laufzeitfehler '438':
Objekt unterstützt diese Eigenschaft oder Methode nicht
Die 3 Funktionen erwarten einen Parameter vom Typ Mandant (was auch im Access angezeigt wird) und der goMandant ist vom Typ Mandant. Ich weiß im Moment nicht wo das Problem liegen kann, ich versuche das schon den ganzen Tag zum Laufen zu bringen, leider ohne Erfolg. Ich muss auch dazu sagen, dass ich vorher noch nie COM-Objekte erstellt habe.
MfG Ace